Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | // Copyright (c) 2008 The Chromium Authors. All rights reserved. |
michael@0 | 2 | // Use of this source code is governed by a BSD-style license that can be |
michael@0 | 3 | // found in the LICENSE file. |
michael@0 | 4 | |
michael@0 | 5 | #include "chrome/common/ipc_channel_posix.h" |
michael@0 | 6 | |
michael@0 | 7 | #include <errno.h> |
michael@0 | 8 | #include <fcntl.h> |
michael@0 | 9 | #include <stddef.h> |
michael@0 | 10 | #include <unistd.h> |
michael@0 | 11 | #include <sys/types.h> |
michael@0 | 12 | #include <sys/socket.h> |
michael@0 | 13 | #include <sys/stat.h> |
michael@0 | 14 | #include <sys/un.h> |
michael@0 | 15 | #include <sys/uio.h> |
michael@0 | 16 | |
michael@0 | 17 | #include <string> |
michael@0 | 18 | #include <map> |
michael@0 | 19 | |
michael@0 | 20 | #include "base/command_line.h" |
michael@0 | 21 | #include "base/eintr_wrapper.h" |
michael@0 | 22 | #include "base/lock.h" |
michael@0 | 23 | #include "base/logging.h" |
michael@0 | 24 | #include "base/process_util.h" |
michael@0 | 25 | #include "base/scoped_ptr.h" |
michael@0 | 26 | #include "base/string_util.h" |
michael@0 | 27 | #include "base/singleton.h" |
michael@0 | 28 | #include "base/stats_counters.h" |
michael@0 | 29 | #include "chrome/common/chrome_switches.h" |
michael@0 | 30 | #include "chrome/common/file_descriptor_set_posix.h" |
michael@0 | 31 | #include "chrome/common/ipc_logging.h" |
michael@0 | 32 | #include "chrome/common/ipc_message_utils.h" |
michael@0 | 33 | #include "mozilla/ipc/ProtocolUtils.h" |
michael@0 | 34 | |
michael@0 | 35 | #ifdef MOZ_TASK_TRACER |
michael@0 | 36 | #include "GeckoTaskTracerImpl.h" |
michael@0 | 37 | using namespace mozilla::tasktracer; |
michael@0 | 38 | #endif |
michael@0 | 39 | |
michael@0 | 40 | namespace IPC { |
michael@0 | 41 | |
michael@0 | 42 | // IPC channels on Windows use named pipes (CreateNamedPipe()) with |
michael@0 | 43 | // channel ids as the pipe names. Channels on POSIX use anonymous |
michael@0 | 44 | // Unix domain sockets created via socketpair() as pipes. These don't |
michael@0 | 45 | // quite line up. |
michael@0 | 46 | // |
michael@0 | 47 | // When creating a child subprocess, the parent side of the fork |
michael@0 | 48 | // arranges it such that the initial control channel ends up on the |
michael@0 | 49 | // magic file descriptor kClientChannelFd in the child. Future |
michael@0 | 50 | // connections (file descriptors) can then be passed via that |
michael@0 | 51 | // connection via sendmsg(). |
michael@0 | 52 | |
michael@0 | 53 | //------------------------------------------------------------------------------ |
michael@0 | 54 | namespace { |
michael@0 | 55 | |
michael@0 | 56 | // The PipeMap class works around this quirk related to unit tests: |
michael@0 | 57 | // |
michael@0 | 58 | // When running as a server, we install the client socket in a |
michael@0 | 59 | // specific file descriptor number (@kClientChannelFd). However, we |
michael@0 | 60 | // also have to support the case where we are running unittests in the |
michael@0 | 61 | // same process. (We do not support forking without execing.) |
michael@0 | 62 | // |
michael@0 | 63 | // Case 1: normal running |
michael@0 | 64 | // The IPC server object will install a mapping in PipeMap from the |
michael@0 | 65 | // name which it was given to the client pipe. When forking the client, the |
michael@0 | 66 | // GetClientFileDescriptorMapping will ensure that the socket is installed in |
michael@0 | 67 | // the magic slot (@kClientChannelFd). The client will search for the |
michael@0 | 68 | // mapping, but it won't find any since we are in a new process. Thus the |
michael@0 | 69 | // magic fd number is returned. Once the client connects, the server will |
michael@0 | 70 | // close its copy of the client socket and remove the mapping. |
michael@0 | 71 | // |
michael@0 | 72 | // Case 2: unittests - client and server in the same process |
michael@0 | 73 | // The IPC server will install a mapping as before. The client will search |
michael@0 | 74 | // for a mapping and find out. It duplicates the file descriptor and |
michael@0 | 75 | // connects. Once the client connects, the server will close the original |
michael@0 | 76 | // copy of the client socket and remove the mapping. Thus, when the client |
michael@0 | 77 | // object closes, it will close the only remaining copy of the client socket |
michael@0 | 78 | // in the fd table and the server will see EOF on its side. |
michael@0 | 79 | // |
michael@0 | 80 | // TODO(port): a client process cannot connect to multiple IPC channels with |
michael@0 | 81 | // this scheme. |
michael@0 | 82 | |
michael@0 | 83 | class PipeMap { |
michael@0 | 84 | public: |
michael@0 | 85 | // Lookup a given channel id. Return -1 if not found. |
michael@0 | 86 | int Lookup(const std::string& channel_id) { |
michael@0 | 87 | AutoLock locked(lock_); |
michael@0 | 88 | |
michael@0 | 89 | ChannelToFDMap::const_iterator i = map_.find(channel_id); |
michael@0 | 90 | if (i == map_.end()) |
michael@0 | 91 | return -1; |
michael@0 | 92 | return i->second; |
michael@0 | 93 | } |
michael@0 | 94 | |
michael@0 | 95 | // Remove the mapping for the given channel id. No error is signaled if the |
michael@0 | 96 | // channel_id doesn't exist |
michael@0 | 97 | void Remove(const std::string& channel_id) { |
michael@0 | 98 | AutoLock locked(lock_); |
michael@0 | 99 | |
michael@0 | 100 | ChannelToFDMap::iterator i = map_.find(channel_id); |
michael@0 | 101 | if (i != map_.end()) |
michael@0 | 102 | map_.erase(i); |
michael@0 | 103 | } |
michael@0 | 104 | |
michael@0 | 105 | // Insert a mapping from @channel_id to @fd. It's a fatal error to insert a |
michael@0 | 106 | // mapping if one already exists for the given channel_id |
michael@0 | 107 | void Insert(const std::string& channel_id, int fd) { |
michael@0 | 108 | AutoLock locked(lock_); |
michael@0 | 109 | DCHECK(fd != -1); |
michael@0 | 110 | |
michael@0 | 111 | ChannelToFDMap::const_iterator i = map_.find(channel_id); |
michael@0 | 112 | CHECK(i == map_.end()) << "Creating second IPC server for '" |
michael@0 | 113 | << channel_id |
michael@0 | 114 | << "' while first still exists"; |
michael@0 | 115 | map_[channel_id] = fd; |
michael@0 | 116 | } |
michael@0 | 117 | |
michael@0 | 118 | private: |
michael@0 | 119 | Lock lock_; |
michael@0 | 120 | typedef std::map<std::string, int> ChannelToFDMap; |
michael@0 | 121 | ChannelToFDMap map_; |
michael@0 | 122 | }; |
michael@0 | 123 | |
michael@0 | 124 | // This is the file descriptor number that a client process expects to find its |
michael@0 | 125 | // IPC socket. |
michael@0 | 126 | static const int kClientChannelFd = 3; |
michael@0 | 127 | |
michael@0 | 128 | // Used to map a channel name to the equivalent FD # in the client process. |
michael@0 | 129 | int ChannelNameToClientFD(const std::string& channel_id) { |
michael@0 | 130 | // See the large block comment above PipeMap for the reasoning here. |
michael@0 | 131 | const int fd = Singleton<PipeMap>()->Lookup(channel_id); |
michael@0 | 132 | if (fd != -1) |
michael@0 | 133 | return dup(fd); |
michael@0 | 134 | |
michael@0 | 135 | // If we don't find an entry, we assume that the correct value has been |
michael@0 | 136 | // inserted in the magic slot. |
michael@0 | 137 | return kClientChannelFd; |
michael@0 | 138 | } |
michael@0 | 139 | |
michael@0 | 140 | //------------------------------------------------------------------------------ |
michael@0 | 141 | const size_t kMaxPipeNameLength = sizeof(((sockaddr_un*)0)->sun_path); |
michael@0 | 142 | |
michael@0 | 143 | // Creates a Fifo with the specified name ready to listen on. |
michael@0 | 144 | bool CreateServerFifo(const std::string& pipe_name, int* server_listen_fd) { |
michael@0 | 145 | DCHECK(server_listen_fd); |
michael@0 | 146 | DCHECK_GT(pipe_name.length(), 0u); |
michael@0 | 147 | DCHECK_LT(pipe_name.length(), kMaxPipeNameLength); |
michael@0 | 148 | |
michael@0 | 149 | if (pipe_name.length() == 0 || pipe_name.length() >= kMaxPipeNameLength) { |
michael@0 | 150 | return false; |
michael@0 | 151 | } |
michael@0 | 152 | |
michael@0 | 153 | // Create socket. |
michael@0 | 154 | int fd = socket(AF_UNIX, SOCK_STREAM, 0); |
michael@0 | 155 | if (fd < 0) { |
michael@0 | 156 | return false; |
michael@0 | 157 | } |
michael@0 | 158 | |
michael@0 | 159 | // Make socket non-blocking |
michael@0 | 160 | if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { |
michael@0 | 161 | HANDLE_EINTR(close(fd)); |
michael@0 | 162 | return false; |
michael@0 | 163 | } |
michael@0 | 164 | |
michael@0 | 165 | // Delete any old FS instances. |
michael@0 | 166 | unlink(pipe_name.c_str()); |
michael@0 | 167 | |
michael@0 | 168 | // Create unix_addr structure |
michael@0 | 169 | struct sockaddr_un unix_addr; |
michael@0 | 170 | memset(&unix_addr, 0, sizeof(unix_addr)); |
michael@0 | 171 | unix_addr.sun_family = AF_UNIX; |
michael@0 | 172 | snprintf(unix_addr.sun_path, kMaxPipeNameLength, "%s", pipe_name.c_str()); |
michael@0 | 173 | size_t unix_addr_len = offsetof(struct sockaddr_un, sun_path) + |
michael@0 | 174 | strlen(unix_addr.sun_path) + 1; |
michael@0 | 175 | |
michael@0 | 176 | // Bind the socket. |
michael@0 | 177 | if (bind(fd, reinterpret_cast<const sockaddr*>(&unix_addr), |
michael@0 | 178 | unix_addr_len) != 0) { |
michael@0 | 179 | HANDLE_EINTR(close(fd)); |
michael@0 | 180 | return false; |
michael@0 | 181 | } |
michael@0 | 182 | |
michael@0 | 183 | // Start listening on the socket. |
michael@0 | 184 | const int listen_queue_length = 1; |
michael@0 | 185 | if (listen(fd, listen_queue_length) != 0) { |
michael@0 | 186 | HANDLE_EINTR(close(fd)); |
michael@0 | 187 | return false; |
michael@0 | 188 | } |
michael@0 | 189 | |
michael@0 | 190 | *server_listen_fd = fd; |
michael@0 | 191 | return true; |
michael@0 | 192 | } |
michael@0 | 193 | |
michael@0 | 194 | // Accept a connection on a fifo. |
michael@0 | 195 | bool ServerAcceptFifoConnection(int server_listen_fd, int* server_socket) { |
michael@0 | 196 | DCHECK(server_socket); |
michael@0 | 197 | |
michael@0 | 198 | int accept_fd = HANDLE_EINTR(accept(server_listen_fd, NULL, 0)); |
michael@0 | 199 | if (accept_fd < 0) |
michael@0 | 200 | return false; |
michael@0 | 201 | if (fcntl(accept_fd, F_SETFL, O_NONBLOCK) == -1) { |
michael@0 | 202 | HANDLE_EINTR(close(accept_fd)); |
michael@0 | 203 | return false; |
michael@0 | 204 | } |
michael@0 | 205 | |
michael@0 | 206 | *server_socket = accept_fd; |
michael@0 | 207 | return true; |
michael@0 | 208 | } |
michael@0 | 209 | |
michael@0 | 210 | bool ClientConnectToFifo(const std::string &pipe_name, int* client_socket) { |
michael@0 | 211 | DCHECK(client_socket); |
michael@0 | 212 | DCHECK_LT(pipe_name.length(), kMaxPipeNameLength); |
michael@0 | 213 | |
michael@0 | 214 | // Create socket. |
michael@0 | 215 | int fd = socket(AF_UNIX, SOCK_STREAM, 0); |
michael@0 | 216 | if (fd < 0) { |
michael@0 | 217 | CHROMIUM_LOG(ERROR) << "fd is invalid"; |
michael@0 | 218 | return false; |
michael@0 | 219 | } |
michael@0 | 220 | |
michael@0 | 221 | // Make socket non-blocking |
michael@0 | 222 | if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { |
michael@0 | 223 | CHROMIUM_LOG(ERROR) << "fcntl failed"; |
michael@0 | 224 | HANDLE_EINTR(close(fd)); |
michael@0 | 225 | return false; |
michael@0 | 226 | } |
michael@0 | 227 | |
michael@0 | 228 | // Create server side of socket. |
michael@0 | 229 | struct sockaddr_un server_unix_addr; |
michael@0 | 230 | memset(&server_unix_addr, 0, sizeof(server_unix_addr)); |
michael@0 | 231 | server_unix_addr.sun_family = AF_UNIX; |
michael@0 | 232 | snprintf(server_unix_addr.sun_path, kMaxPipeNameLength, "%s", |
michael@0 | 233 | pipe_name.c_str()); |
michael@0 | 234 | size_t server_unix_addr_len = offsetof(struct sockaddr_un, sun_path) + |
michael@0 | 235 | strlen(server_unix_addr.sun_path) + 1; |
michael@0 | 236 | |
michael@0 | 237 | if (HANDLE_EINTR(connect(fd, reinterpret_cast<sockaddr*>(&server_unix_addr), |
michael@0 | 238 | server_unix_addr_len)) != 0) { |
michael@0 | 239 | HANDLE_EINTR(close(fd)); |
michael@0 | 240 | return false; |
michael@0 | 241 | } |
michael@0 | 242 | |
michael@0 | 243 | *client_socket = fd; |
michael@0 | 244 | return true; |
michael@0 | 245 | } |
michael@0 | 246 | |
michael@0 | 247 | bool SetCloseOnExec(int fd) { |
michael@0 | 248 | int flags = fcntl(fd, F_GETFD); |
michael@0 | 249 | if (flags == -1) |
michael@0 | 250 | return false; |
michael@0 | 251 | |
michael@0 | 252 | flags |= FD_CLOEXEC; |
michael@0 | 253 | if (fcntl(fd, F_SETFD, flags) == -1) |
michael@0 | 254 | return false; |
michael@0 | 255 | |
michael@0 | 256 | return true; |
michael@0 | 257 | } |
michael@0 | 258 | |
michael@0 | 259 | } // namespace |
michael@0 | 260 | //------------------------------------------------------------------------------ |
michael@0 | 261 | |
michael@0 | 262 | Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id, Mode mode, |
michael@0 | 263 | Listener* listener) |
michael@0 | 264 | : factory_(this) { |
michael@0 | 265 | Init(mode, listener); |
michael@0 | 266 | uses_fifo_ = CommandLine::ForCurrentProcess()->HasSwitch(switches::kIPCUseFIFO); |
michael@0 | 267 | |
michael@0 | 268 | if (!CreatePipe(channel_id, mode)) { |
michael@0 | 269 | // The pipe may have been closed already. |
michael@0 | 270 | CHROMIUM_LOG(WARNING) << "Unable to create pipe named \"" << channel_id << |
michael@0 | 271 | "\" in " << (mode == MODE_SERVER ? "server" : "client") << |
michael@0 | 272 | " mode error(" << strerror(errno) << ")."; |
michael@0 | 273 | } |
michael@0 | 274 | } |
michael@0 | 275 | |
michael@0 | 276 | Channel::ChannelImpl::ChannelImpl(int fd, Mode mode, Listener* listener) |
michael@0 | 277 | : factory_(this) { |
michael@0 | 278 | Init(mode, listener); |
michael@0 | 279 | pipe_ = fd; |
michael@0 | 280 | waiting_connect_ = (MODE_SERVER == mode); |
michael@0 | 281 | |
michael@0 | 282 | EnqueueHelloMessage(); |
michael@0 | 283 | } |
michael@0 | 284 | |
michael@0 | 285 | void Channel::ChannelImpl::Init(Mode mode, Listener* listener) { |
michael@0 | 286 | mode_ = mode; |
michael@0 | 287 | is_blocked_on_write_ = false; |
michael@0 | 288 | message_send_bytes_written_ = 0; |
michael@0 | 289 | uses_fifo_ = false; |
michael@0 | 290 | server_listen_pipe_ = -1; |
michael@0 | 291 | pipe_ = -1; |
michael@0 | 292 | client_pipe_ = -1; |
michael@0 | 293 | listener_ = listener; |
michael@0 | 294 | waiting_connect_ = true; |
michael@0 | 295 | processing_incoming_ = false; |
michael@0 | 296 | closed_ = false; |
michael@0 | 297 | #if defined(OS_MACOSX) |
michael@0 | 298 | last_pending_fd_id_ = 0; |
michael@0 | 299 | #endif |
michael@0 | 300 | output_queue_length_ = 0; |
michael@0 | 301 | } |
michael@0 | 302 | |
michael@0 | 303 | bool Channel::ChannelImpl::CreatePipe(const std::wstring& channel_id, |
michael@0 | 304 | Mode mode) { |
michael@0 | 305 | DCHECK(server_listen_pipe_ == -1 && pipe_ == -1); |
michael@0 | 306 | |
michael@0 | 307 | if (uses_fifo_) { |
michael@0 | 308 | // This only happens in unit tests; see the comment above PipeMap. |
michael@0 | 309 | // TODO(playmobil): We shouldn't need to create fifos on disk. |
michael@0 | 310 | // TODO(playmobil): If we do, they should be in the user data directory. |
michael@0 | 311 | // TODO(playmobil): Cleanup any stale fifos. |
michael@0 | 312 | pipe_name_ = "/var/tmp/chrome_" + WideToASCII(channel_id); |
michael@0 | 313 | if (mode == MODE_SERVER) { |
michael@0 | 314 | if (!CreateServerFifo(pipe_name_, &server_listen_pipe_)) { |
michael@0 | 315 | return false; |
michael@0 | 316 | } |
michael@0 | 317 | } else { |
michael@0 | 318 | if (!ClientConnectToFifo(pipe_name_, &pipe_)) { |
michael@0 | 319 | return false; |
michael@0 | 320 | } |
michael@0 | 321 | waiting_connect_ = false; |
michael@0 | 322 | } |
michael@0 | 323 | } else { |
michael@0 | 324 | // socketpair() |
michael@0 | 325 | pipe_name_ = WideToASCII(channel_id); |
michael@0 | 326 | if (mode == MODE_SERVER) { |
michael@0 | 327 | int pipe_fds[2]; |
michael@0 | 328 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds) != 0) { |
michael@0 | 329 | return false; |
michael@0 | 330 | } |
michael@0 | 331 | // Set both ends to be non-blocking. |
michael@0 | 332 | if (fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK) == -1 || |
michael@0 | 333 | fcntl(pipe_fds[1], F_SETFL, O_NONBLOCK) == -1) { |
michael@0 | 334 | HANDLE_EINTR(close(pipe_fds[0])); |
michael@0 | 335 | HANDLE_EINTR(close(pipe_fds[1])); |
michael@0 | 336 | return false; |
michael@0 | 337 | } |
michael@0 | 338 | |
michael@0 | 339 | if (!SetCloseOnExec(pipe_fds[0]) || |
michael@0 | 340 | !SetCloseOnExec(pipe_fds[1])) { |
michael@0 | 341 | HANDLE_EINTR(close(pipe_fds[0])); |
michael@0 | 342 | HANDLE_EINTR(close(pipe_fds[1])); |
michael@0 | 343 | return false; |
michael@0 | 344 | } |
michael@0 | 345 | |
michael@0 | 346 | pipe_ = pipe_fds[0]; |
michael@0 | 347 | client_pipe_ = pipe_fds[1]; |
michael@0 | 348 | |
michael@0 | 349 | if (pipe_name_.length()) { |
michael@0 | 350 | Singleton<PipeMap>()->Insert(pipe_name_, client_pipe_); |
michael@0 | 351 | } |
michael@0 | 352 | } else { |
michael@0 | 353 | pipe_ = ChannelNameToClientFD(pipe_name_); |
michael@0 | 354 | DCHECK(pipe_ > 0); |
michael@0 | 355 | waiting_connect_ = false; |
michael@0 | 356 | } |
michael@0 | 357 | } |
michael@0 | 358 | |
michael@0 | 359 | // Create the Hello message to be sent when Connect is called |
michael@0 | 360 | return EnqueueHelloMessage(); |
michael@0 | 361 | } |
michael@0 | 362 | |
michael@0 | 363 | /** |
michael@0 | 364 | * Reset the file descriptor for communication with the peer. |
michael@0 | 365 | */ |
michael@0 | 366 | void Channel::ChannelImpl::ResetFileDescriptor(int fd) { |
michael@0 | 367 | NS_ASSERTION(fd > 0 && fd == pipe_, "Invalid file descriptor"); |
michael@0 | 368 | |
michael@0 | 369 | EnqueueHelloMessage(); |
michael@0 | 370 | } |
michael@0 | 371 | |
michael@0 | 372 | bool Channel::ChannelImpl::EnqueueHelloMessage() { |
michael@0 | 373 | scoped_ptr<Message> msg(new Message(MSG_ROUTING_NONE, |
michael@0 | 374 | HELLO_MESSAGE_TYPE, |
michael@0 | 375 | IPC::Message::PRIORITY_NORMAL)); |
michael@0 | 376 | if (!msg->WriteInt(base::GetCurrentProcId())) { |
michael@0 | 377 | Close(); |
michael@0 | 378 | return false; |
michael@0 | 379 | } |
michael@0 | 380 | |
michael@0 | 381 | OutputQueuePush(msg.release()); |
michael@0 | 382 | return true; |
michael@0 | 383 | } |
michael@0 | 384 | |
michael@0 | 385 | static void |
michael@0 | 386 | ClearAndShrink(std::string& s, size_t capacity) |
michael@0 | 387 | { |
michael@0 | 388 | // This swap trick is the closest thing C++ has to a guaranteed way to |
michael@0 | 389 | // shrink the capacity of a string. |
michael@0 | 390 | std::string tmp; |
michael@0 | 391 | tmp.reserve(capacity); |
michael@0 | 392 | s.swap(tmp); |
michael@0 | 393 | } |
michael@0 | 394 | |
michael@0 | 395 | bool Channel::ChannelImpl::Connect() { |
michael@0 | 396 | if (mode_ == MODE_SERVER && uses_fifo_) { |
michael@0 | 397 | if (server_listen_pipe_ == -1) { |
michael@0 | 398 | return false; |
michael@0 | 399 | } |
michael@0 | 400 | MessageLoopForIO::current()->WatchFileDescriptor( |
michael@0 | 401 | server_listen_pipe_, |
michael@0 | 402 | true, |
michael@0 | 403 | MessageLoopForIO::WATCH_READ, |
michael@0 | 404 | &server_listen_connection_watcher_, |
michael@0 | 405 | this); |
michael@0 | 406 | } else { |
michael@0 | 407 | if (pipe_ == -1) { |
michael@0 | 408 | return false; |
michael@0 | 409 | } |
michael@0 | 410 | MessageLoopForIO::current()->WatchFileDescriptor( |
michael@0 | 411 | pipe_, |
michael@0 | 412 | true, |
michael@0 | 413 | MessageLoopForIO::WATCH_READ, |
michael@0 | 414 | &read_watcher_, |
michael@0 | 415 | this); |
michael@0 | 416 | waiting_connect_ = false; |
michael@0 | 417 | } |
michael@0 | 418 | |
michael@0 | 419 | if (!waiting_connect_) |
michael@0 | 420 | return ProcessOutgoingMessages(); |
michael@0 | 421 | return true; |
michael@0 | 422 | } |
michael@0 | 423 | |
michael@0 | 424 | bool Channel::ChannelImpl::ProcessIncomingMessages() { |
michael@0 | 425 | ssize_t bytes_read = 0; |
michael@0 | 426 | |
michael@0 | 427 | struct msghdr msg = {0}; |
michael@0 | 428 | struct iovec iov = {input_buf_, Channel::kReadBufferSize}; |
michael@0 | 429 | |
michael@0 | 430 | msg.msg_iov = &iov; |
michael@0 | 431 | msg.msg_iovlen = 1; |
michael@0 | 432 | msg.msg_control = input_cmsg_buf_; |
michael@0 | 433 | |
michael@0 | 434 | for (;;) { |
michael@0 | 435 | msg.msg_controllen = sizeof(input_cmsg_buf_); |
michael@0 | 436 | |
michael@0 | 437 | if (bytes_read == 0) { |
michael@0 | 438 | if (pipe_ == -1) |
michael@0 | 439 | return false; |
michael@0 | 440 | |
michael@0 | 441 | // Read from pipe. |
michael@0 | 442 | // recvmsg() returns 0 if the connection has closed or EAGAIN if no data |
michael@0 | 443 | // is waiting on the pipe. |
michael@0 | 444 | bytes_read = HANDLE_EINTR(recvmsg(pipe_, &msg, MSG_DONTWAIT)); |
michael@0 | 445 | |
michael@0 | 446 | if (bytes_read < 0) { |
michael@0 | 447 | if (errno == EAGAIN) { |
michael@0 | 448 | return true; |
michael@0 | 449 | } else { |
michael@0 | 450 | CHROMIUM_LOG(ERROR) << "pipe error (" << pipe_ << "): " << strerror(errno); |
michael@0 | 451 | return false; |
michael@0 | 452 | } |
michael@0 | 453 | } else if (bytes_read == 0) { |
michael@0 | 454 | // The pipe has closed... |
michael@0 | 455 | Close(); |
michael@0 | 456 | return false; |
michael@0 | 457 | } |
michael@0 | 458 | } |
michael@0 | 459 | DCHECK(bytes_read); |
michael@0 | 460 | |
michael@0 | 461 | if (client_pipe_ != -1) { |
michael@0 | 462 | Singleton<PipeMap>()->Remove(pipe_name_); |
michael@0 | 463 | HANDLE_EINTR(close(client_pipe_)); |
michael@0 | 464 | client_pipe_ = -1; |
michael@0 | 465 | } |
michael@0 | 466 | |
michael@0 | 467 | // a pointer to an array of |num_wire_fds| file descriptors from the read |
michael@0 | 468 | const int* wire_fds = NULL; |
michael@0 | 469 | unsigned num_wire_fds = 0; |
michael@0 | 470 | |
michael@0 | 471 | // walk the list of control messages and, if we find an array of file |
michael@0 | 472 | // descriptors, save a pointer to the array |
michael@0 | 473 | |
michael@0 | 474 | // This next if statement is to work around an OSX issue where |
michael@0 | 475 | // CMSG_FIRSTHDR will return non-NULL in the case that controllen == 0. |
michael@0 | 476 | // Here's a test case: |
michael@0 | 477 | // |
michael@0 | 478 | // int main() { |
michael@0 | 479 | // struct msghdr msg; |
michael@0 | 480 | // msg.msg_control = &msg; |
michael@0 | 481 | // msg.msg_controllen = 0; |
michael@0 | 482 | // if (CMSG_FIRSTHDR(&msg)) |
michael@0 | 483 | // printf("Bug found!\n"); |
michael@0 | 484 | // } |
michael@0 | 485 | if (msg.msg_controllen > 0) { |
michael@0 | 486 | // On OSX, CMSG_FIRSTHDR doesn't handle the case where controllen is 0 |
michael@0 | 487 | // and will return a pointer into nowhere. |
michael@0 | 488 | for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg; |
michael@0 | 489 | cmsg = CMSG_NXTHDR(&msg, cmsg)) { |
michael@0 | 490 | if (cmsg->cmsg_level == SOL_SOCKET && |
michael@0 | 491 | cmsg->cmsg_type == SCM_RIGHTS) { |
michael@0 | 492 | const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0); |
michael@0 | 493 | DCHECK(payload_len % sizeof(int) == 0); |
michael@0 | 494 | wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg)); |
michael@0 | 495 | num_wire_fds = payload_len / 4; |
michael@0 | 496 | |
michael@0 | 497 | if (msg.msg_flags & MSG_CTRUNC) { |
michael@0 | 498 | CHROMIUM_LOG(ERROR) << "SCM_RIGHTS message was truncated" |
michael@0 | 499 | << " cmsg_len:" << cmsg->cmsg_len |
michael@0 | 500 | << " fd:" << pipe_; |
michael@0 | 501 | for (unsigned i = 0; i < num_wire_fds; ++i) |
michael@0 | 502 | HANDLE_EINTR(close(wire_fds[i])); |
michael@0 | 503 | return false; |
michael@0 | 504 | } |
michael@0 | 505 | break; |
michael@0 | 506 | } |
michael@0 | 507 | } |
michael@0 | 508 | } |
michael@0 | 509 | |
michael@0 | 510 | // Process messages from input buffer. |
michael@0 | 511 | const char *p; |
michael@0 | 512 | const char *overflowp; |
michael@0 | 513 | const char *end; |
michael@0 | 514 | if (input_overflow_buf_.empty()) { |
michael@0 | 515 | overflowp = NULL; |
michael@0 | 516 | p = input_buf_; |
michael@0 | 517 | end = p + bytes_read; |
michael@0 | 518 | } else { |
michael@0 | 519 | if (input_overflow_buf_.size() > |
michael@0 | 520 | static_cast<size_t>(kMaximumMessageSize - bytes_read)) { |
michael@0 | 521 | ClearAndShrink(input_overflow_buf_, Channel::kReadBufferSize); |
michael@0 | 522 | CHROMIUM_LOG(ERROR) << "IPC message is too big"; |
michael@0 | 523 | return false; |
michael@0 | 524 | } |
michael@0 | 525 | input_overflow_buf_.append(input_buf_, bytes_read); |
michael@0 | 526 | overflowp = p = input_overflow_buf_.data(); |
michael@0 | 527 | end = p + input_overflow_buf_.size(); |
michael@0 | 528 | } |
michael@0 | 529 | |
michael@0 | 530 | // A pointer to an array of |num_fds| file descriptors which includes any |
michael@0 | 531 | // fds that have spilled over from a previous read. |
michael@0 | 532 | const int* fds; |
michael@0 | 533 | unsigned num_fds; |
michael@0 | 534 | unsigned fds_i = 0; // the index of the first unused descriptor |
michael@0 | 535 | |
michael@0 | 536 | if (input_overflow_fds_.empty()) { |
michael@0 | 537 | fds = wire_fds; |
michael@0 | 538 | num_fds = num_wire_fds; |
michael@0 | 539 | } else { |
michael@0 | 540 | const size_t prev_size = input_overflow_fds_.size(); |
michael@0 | 541 | input_overflow_fds_.resize(prev_size + num_wire_fds); |
michael@0 | 542 | memcpy(&input_overflow_fds_[prev_size], wire_fds, |
michael@0 | 543 | num_wire_fds * sizeof(int)); |
michael@0 | 544 | fds = &input_overflow_fds_[0]; |
michael@0 | 545 | num_fds = input_overflow_fds_.size(); |
michael@0 | 546 | } |
michael@0 | 547 | |
michael@0 | 548 | while (p < end) { |
michael@0 | 549 | const char* message_tail = Message::FindNext(p, end); |
michael@0 | 550 | if (message_tail) { |
michael@0 | 551 | int len = static_cast<int>(message_tail - p); |
michael@0 | 552 | Message m(p, len); |
michael@0 | 553 | if (m.header()->num_fds) { |
michael@0 | 554 | // the message has file descriptors |
michael@0 | 555 | const char* error = NULL; |
michael@0 | 556 | if (m.header()->num_fds > num_fds - fds_i) { |
michael@0 | 557 | // the message has been completely received, but we didn't get |
michael@0 | 558 | // enough file descriptors. |
michael@0 | 559 | error = "Message needs unreceived descriptors"; |
michael@0 | 560 | } |
michael@0 | 561 | |
michael@0 | 562 | if (m.header()->num_fds > |
michael@0 | 563 | FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE) { |
michael@0 | 564 | // There are too many descriptors in this message |
michael@0 | 565 | error = "Message requires an excessive number of descriptors"; |
michael@0 | 566 | } |
michael@0 | 567 | |
michael@0 | 568 | if (error) { |
michael@0 | 569 | CHROMIUM_LOG(WARNING) << error |
michael@0 | 570 | << " channel:" << this |
michael@0 | 571 | << " message-type:" << m.type() |
michael@0 | 572 | << " header()->num_fds:" << m.header()->num_fds |
michael@0 | 573 | << " num_fds:" << num_fds |
michael@0 | 574 | << " fds_i:" << fds_i; |
michael@0 | 575 | // close the existing file descriptors so that we don't leak them |
michael@0 | 576 | for (unsigned i = fds_i; i < num_fds; ++i) |
michael@0 | 577 | HANDLE_EINTR(close(fds[i])); |
michael@0 | 578 | input_overflow_fds_.clear(); |
michael@0 | 579 | // abort the connection |
michael@0 | 580 | return false; |
michael@0 | 581 | } |
michael@0 | 582 | |
michael@0 | 583 | #if defined(OS_MACOSX) |
michael@0 | 584 | // Send a message to the other side, indicating that we are now |
michael@0 | 585 | // responsible for closing the descriptor. |
michael@0 | 586 | Message *fdAck = new Message(MSG_ROUTING_NONE, |
michael@0 | 587 | RECEIVED_FDS_MESSAGE_TYPE, |
michael@0 | 588 | IPC::Message::PRIORITY_NORMAL); |
michael@0 | 589 | DCHECK(m.fd_cookie() != 0); |
michael@0 | 590 | fdAck->set_fd_cookie(m.fd_cookie()); |
michael@0 | 591 | OutputQueuePush(fdAck); |
michael@0 | 592 | #endif |
michael@0 | 593 | |
michael@0 | 594 | m.file_descriptor_set()->SetDescriptors( |
michael@0 | 595 | &fds[fds_i], m.header()->num_fds); |
michael@0 | 596 | fds_i += m.header()->num_fds; |
michael@0 | 597 | } |
michael@0 | 598 | #ifdef IPC_MESSAGE_DEBUG_EXTRA |
michael@0 | 599 | DLOG(INFO) << "received message on channel @" << this << |
michael@0 | 600 | " with type " << m.type(); |
michael@0 | 601 | #endif |
michael@0 | 602 | |
michael@0 | 603 | #ifdef MOZ_TASK_TRACER |
michael@0 | 604 | AutoSaveCurTraceInfo saveCurTraceInfo; |
michael@0 | 605 | SetCurTraceInfo(m.header()->source_event_id, |
michael@0 | 606 | m.header()->parent_task_id, |
michael@0 | 607 | m.header()->source_event_type); |
michael@0 | 608 | #endif |
michael@0 | 609 | |
michael@0 | 610 | if (m.routing_id() == MSG_ROUTING_NONE && |
michael@0 | 611 | m.type() == HELLO_MESSAGE_TYPE) { |
michael@0 | 612 | // The Hello message contains only the process id. |
michael@0 | 613 | listener_->OnChannelConnected(MessageIterator(m).NextInt()); |
michael@0 | 614 | #if defined(OS_MACOSX) |
michael@0 | 615 | } else if (m.routing_id() == MSG_ROUTING_NONE && |
michael@0 | 616 | m.type() == RECEIVED_FDS_MESSAGE_TYPE) { |
michael@0 | 617 | DCHECK(m.fd_cookie() != 0); |
michael@0 | 618 | CloseDescriptors(m.fd_cookie()); |
michael@0 | 619 | #endif |
michael@0 | 620 | } else { |
michael@0 | 621 | listener_->OnMessageReceived(m); |
michael@0 | 622 | } |
michael@0 | 623 | p = message_tail; |
michael@0 | 624 | } else { |
michael@0 | 625 | // Last message is partial. |
michael@0 | 626 | break; |
michael@0 | 627 | } |
michael@0 | 628 | } |
michael@0 | 629 | if (end == p) { |
michael@0 | 630 | ClearAndShrink(input_overflow_buf_, Channel::kReadBufferSize); |
michael@0 | 631 | } else if (!overflowp) { |
michael@0 | 632 | // p is from input_buf_ |
michael@0 | 633 | input_overflow_buf_.assign(p, end - p); |
michael@0 | 634 | } else if (p > overflowp) { |
michael@0 | 635 | // p is from input_overflow_buf_ |
michael@0 | 636 | input_overflow_buf_.erase(0, p - overflowp); |
michael@0 | 637 | } |
michael@0 | 638 | input_overflow_fds_ = std::vector<int>(&fds[fds_i], &fds[num_fds]); |
michael@0 | 639 | |
michael@0 | 640 | // When the input data buffer is empty, the overflow fds should be too. If |
michael@0 | 641 | // this is not the case, we probably have a rogue renderer which is trying |
michael@0 | 642 | // to fill our descriptor table. |
michael@0 | 643 | if (input_overflow_buf_.empty() && !input_overflow_fds_.empty()) { |
michael@0 | 644 | // We close these descriptors in Close() |
michael@0 | 645 | return false; |
michael@0 | 646 | } |
michael@0 | 647 | |
michael@0 | 648 | bytes_read = 0; // Get more data. |
michael@0 | 649 | } |
michael@0 | 650 | |
michael@0 | 651 | return true; |
michael@0 | 652 | } |
michael@0 | 653 | |
michael@0 | 654 | bool Channel::ChannelImpl::ProcessOutgoingMessages() { |
michael@0 | 655 | DCHECK(!waiting_connect_); // Why are we trying to send messages if there's |
michael@0 | 656 | // no connection? |
michael@0 | 657 | is_blocked_on_write_ = false; |
michael@0 | 658 | |
michael@0 | 659 | if (output_queue_.empty()) |
michael@0 | 660 | return true; |
michael@0 | 661 | |
michael@0 | 662 | if (pipe_ == -1) |
michael@0 | 663 | return false; |
michael@0 | 664 | |
michael@0 | 665 | // Write out all the messages we can till the write blocks or there are no |
michael@0 | 666 | // more outgoing messages. |
michael@0 | 667 | while (!output_queue_.empty()) { |
michael@0 | 668 | Message* msg = output_queue_.front(); |
michael@0 | 669 | |
michael@0 | 670 | struct msghdr msgh = {0}; |
michael@0 | 671 | |
michael@0 | 672 | static const int tmp = CMSG_SPACE(sizeof( |
michael@0 | 673 | int[FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE])); |
michael@0 | 674 | char buf[tmp]; |
michael@0 | 675 | |
michael@0 | 676 | if (message_send_bytes_written_ == 0 && |
michael@0 | 677 | !msg->file_descriptor_set()->empty()) { |
michael@0 | 678 | // This is the first chunk of a message which has descriptors to send |
michael@0 | 679 | struct cmsghdr *cmsg; |
michael@0 | 680 | const unsigned num_fds = msg->file_descriptor_set()->size(); |
michael@0 | 681 | |
michael@0 | 682 | if (num_fds > FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE) { |
michael@0 | 683 | CHROMIUM_LOG(FATAL) << "Too many file descriptors!"; |
michael@0 | 684 | // This should not be reached. |
michael@0 | 685 | return false; |
michael@0 | 686 | } |
michael@0 | 687 | |
michael@0 | 688 | msgh.msg_control = buf; |
michael@0 | 689 | msgh.msg_controllen = CMSG_SPACE(sizeof(int) * num_fds); |
michael@0 | 690 | cmsg = CMSG_FIRSTHDR(&msgh); |
michael@0 | 691 | cmsg->cmsg_level = SOL_SOCKET; |
michael@0 | 692 | cmsg->cmsg_type = SCM_RIGHTS; |
michael@0 | 693 | cmsg->cmsg_len = CMSG_LEN(sizeof(int) * num_fds); |
michael@0 | 694 | msg->file_descriptor_set()->GetDescriptors( |
michael@0 | 695 | reinterpret_cast<int*>(CMSG_DATA(cmsg))); |
michael@0 | 696 | msgh.msg_controllen = cmsg->cmsg_len; |
michael@0 | 697 | |
michael@0 | 698 | msg->header()->num_fds = num_fds; |
michael@0 | 699 | #if defined(OS_MACOSX) |
michael@0 | 700 | msg->set_fd_cookie(++last_pending_fd_id_); |
michael@0 | 701 | #endif |
michael@0 | 702 | } |
michael@0 | 703 | #ifdef MOZ_TASK_TRACER |
michael@0 | 704 | GetCurTraceInfo(&msg->header()->source_event_id, |
michael@0 | 705 | &msg->header()->parent_task_id, |
michael@0 | 706 | &msg->header()->source_event_type); |
michael@0 | 707 | #endif |
michael@0 | 708 | |
michael@0 | 709 | size_t amt_to_write = msg->size() - message_send_bytes_written_; |
michael@0 | 710 | DCHECK(amt_to_write != 0); |
michael@0 | 711 | const char *out_bytes = reinterpret_cast<const char*>(msg->data()) + |
michael@0 | 712 | message_send_bytes_written_; |
michael@0 | 713 | |
michael@0 | 714 | struct iovec iov = {const_cast<char*>(out_bytes), amt_to_write}; |
michael@0 | 715 | msgh.msg_iov = &iov; |
michael@0 | 716 | msgh.msg_iovlen = 1; |
michael@0 | 717 | |
michael@0 | 718 | ssize_t bytes_written = HANDLE_EINTR(sendmsg(pipe_, &msgh, MSG_DONTWAIT)); |
michael@0 | 719 | #if !defined(OS_MACOSX) |
michael@0 | 720 | // On OSX CommitAll gets called later, once we get the RECEIVED_FDS_MESSAGE_TYPE |
michael@0 | 721 | // message. |
michael@0 | 722 | if (bytes_written > 0) |
michael@0 | 723 | msg->file_descriptor_set()->CommitAll(); |
michael@0 | 724 | #endif |
michael@0 | 725 | |
michael@0 | 726 | if (bytes_written < 0 && errno != EAGAIN) { |
michael@0 | 727 | CHROMIUM_LOG(ERROR) << "pipe error: " << strerror(errno); |
michael@0 | 728 | return false; |
michael@0 | 729 | } |
michael@0 | 730 | |
michael@0 | 731 | if (static_cast<size_t>(bytes_written) != amt_to_write) { |
michael@0 | 732 | if (bytes_written > 0) { |
michael@0 | 733 | // If write() fails with EAGAIN then bytes_written will be -1. |
michael@0 | 734 | message_send_bytes_written_ += bytes_written; |
michael@0 | 735 | } |
michael@0 | 736 | |
michael@0 | 737 | // Tell libevent to call us back once things are unblocked. |
michael@0 | 738 | is_blocked_on_write_ = true; |
michael@0 | 739 | MessageLoopForIO::current()->WatchFileDescriptor( |
michael@0 | 740 | pipe_, |
michael@0 | 741 | false, // One shot |
michael@0 | 742 | MessageLoopForIO::WATCH_WRITE, |
michael@0 | 743 | &write_watcher_, |
michael@0 | 744 | this); |
michael@0 | 745 | return true; |
michael@0 | 746 | } else { |
michael@0 | 747 | message_send_bytes_written_ = 0; |
michael@0 | 748 | |
michael@0 | 749 | #if defined(OS_MACOSX) |
michael@0 | 750 | if (!msg->file_descriptor_set()->empty()) |
michael@0 | 751 | pending_fds_.push_back(PendingDescriptors(msg->fd_cookie(), |
michael@0 | 752 | msg->file_descriptor_set())); |
michael@0 | 753 | #endif |
michael@0 | 754 | |
michael@0 | 755 | // Message sent OK! |
michael@0 | 756 | #ifdef IPC_MESSAGE_DEBUG_EXTRA |
michael@0 | 757 | DLOG(INFO) << "sent message @" << msg << " on channel @" << this << |
michael@0 | 758 | " with type " << msg->type(); |
michael@0 | 759 | #endif |
michael@0 | 760 | OutputQueuePop(); |
michael@0 | 761 | delete msg; |
michael@0 | 762 | } |
michael@0 | 763 | } |
michael@0 | 764 | return true; |
michael@0 | 765 | } |
michael@0 | 766 | |
michael@0 | 767 | bool Channel::ChannelImpl::Send(Message* message) { |
michael@0 | 768 | #ifdef IPC_MESSAGE_DEBUG_EXTRA |
michael@0 | 769 | DLOG(INFO) << "sending message @" << message << " on channel @" << this |
michael@0 | 770 | << " with type " << message->type() |
michael@0 | 771 | << " (" << output_queue_.size() << " in queue)"; |
michael@0 | 772 | #endif |
michael@0 | 773 | |
michael@0 | 774 | #ifdef IPC_MESSAGE_LOG_ENABLED |
michael@0 | 775 | Logging::current()->OnSendMessage(message, L""); |
michael@0 | 776 | #endif |
michael@0 | 777 | |
michael@0 | 778 | // If the channel has been closed, ProcessOutgoingMessages() is never going |
michael@0 | 779 | // to pop anything off output_queue; output_queue will only get emptied when |
michael@0 | 780 | // the channel is destructed. We might as well delete message now, instead |
michael@0 | 781 | // of waiting for the channel to be destructed. |
michael@0 | 782 | if (closed_) { |
michael@0 | 783 | if (mozilla::ipc::LoggingEnabled()) { |
michael@0 | 784 | fprintf(stderr, "Can't send message %s, because this channel is closed.\n", |
michael@0 | 785 | message->name()); |
michael@0 | 786 | } |
michael@0 | 787 | delete message; |
michael@0 | 788 | return false; |
michael@0 | 789 | } |
michael@0 | 790 | |
michael@0 | 791 | OutputQueuePush(message); |
michael@0 | 792 | if (!waiting_connect_) { |
michael@0 | 793 | if (!is_blocked_on_write_) { |
michael@0 | 794 | if (!ProcessOutgoingMessages()) |
michael@0 | 795 | return false; |
michael@0 | 796 | } |
michael@0 | 797 | } |
michael@0 | 798 | |
michael@0 | 799 | return true; |
michael@0 | 800 | } |
michael@0 | 801 | |
michael@0 | 802 | void Channel::ChannelImpl::GetClientFileDescriptorMapping(int *src_fd, |
michael@0 | 803 | int *dest_fd) const { |
michael@0 | 804 | DCHECK(mode_ == MODE_SERVER); |
michael@0 | 805 | *src_fd = client_pipe_; |
michael@0 | 806 | *dest_fd = kClientChannelFd; |
michael@0 | 807 | } |
michael@0 | 808 | |
michael@0 | 809 | void Channel::ChannelImpl::CloseClientFileDescriptor() { |
michael@0 | 810 | if (client_pipe_ != -1) { |
michael@0 | 811 | Singleton<PipeMap>()->Remove(pipe_name_); |
michael@0 | 812 | HANDLE_EINTR(close(client_pipe_)); |
michael@0 | 813 | client_pipe_ = -1; |
michael@0 | 814 | } |
michael@0 | 815 | } |
michael@0 | 816 | |
michael@0 | 817 | // Called by libevent when we can read from th pipe without blocking. |
michael@0 | 818 | void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) { |
michael@0 | 819 | bool send_server_hello_msg = false; |
michael@0 | 820 | if (waiting_connect_ && mode_ == MODE_SERVER) { |
michael@0 | 821 | // In the case of a socketpair() the server starts listening on its end |
michael@0 | 822 | // of the pipe in Connect(). |
michael@0 | 823 | DCHECK(uses_fifo_); |
michael@0 | 824 | |
michael@0 | 825 | if (!ServerAcceptFifoConnection(server_listen_pipe_, &pipe_)) { |
michael@0 | 826 | Close(); |
michael@0 | 827 | } |
michael@0 | 828 | |
michael@0 | 829 | // No need to watch the listening socket any longer since only one client |
michael@0 | 830 | // can connect. So unregister with libevent. |
michael@0 | 831 | server_listen_connection_watcher_.StopWatchingFileDescriptor(); |
michael@0 | 832 | |
michael@0 | 833 | // Start watching our end of the socket. |
michael@0 | 834 | MessageLoopForIO::current()->WatchFileDescriptor( |
michael@0 | 835 | pipe_, |
michael@0 | 836 | true, |
michael@0 | 837 | MessageLoopForIO::WATCH_READ, |
michael@0 | 838 | &read_watcher_, |
michael@0 | 839 | this); |
michael@0 | 840 | |
michael@0 | 841 | waiting_connect_ = false; |
michael@0 | 842 | send_server_hello_msg = true; |
michael@0 | 843 | } |
michael@0 | 844 | |
michael@0 | 845 | if (!waiting_connect_ && fd == pipe_) { |
michael@0 | 846 | if (!ProcessIncomingMessages()) { |
michael@0 | 847 | Close(); |
michael@0 | 848 | listener_->OnChannelError(); |
michael@0 | 849 | } |
michael@0 | 850 | } |
michael@0 | 851 | |
michael@0 | 852 | // If we're a server and handshaking, then we want to make sure that we |
michael@0 | 853 | // only send our handshake message after we've processed the client's. |
michael@0 | 854 | // This gives us a chance to kill the client if the incoming handshake |
michael@0 | 855 | // is invalid. |
michael@0 | 856 | if (send_server_hello_msg) { |
michael@0 | 857 | // This should be our first write so there's no chance we can block here... |
michael@0 | 858 | DCHECK(is_blocked_on_write_ == false); |
michael@0 | 859 | ProcessOutgoingMessages(); |
michael@0 | 860 | } |
michael@0 | 861 | } |
michael@0 | 862 | |
michael@0 | 863 | #if defined(OS_MACOSX) |
michael@0 | 864 | void Channel::ChannelImpl::CloseDescriptors(uint32_t pending_fd_id) |
michael@0 | 865 | { |
michael@0 | 866 | DCHECK(pending_fd_id != 0); |
michael@0 | 867 | for (std::list<PendingDescriptors>::iterator i = pending_fds_.begin(); |
michael@0 | 868 | i != pending_fds_.end(); |
michael@0 | 869 | i++) { |
michael@0 | 870 | if ((*i).id == pending_fd_id) { |
michael@0 | 871 | (*i).fds->CommitAll(); |
michael@0 | 872 | pending_fds_.erase(i); |
michael@0 | 873 | return; |
michael@0 | 874 | } |
michael@0 | 875 | } |
michael@0 | 876 | DCHECK(false) << "pending_fd_id not in our list!"; |
michael@0 | 877 | } |
michael@0 | 878 | #endif |
michael@0 | 879 | |
michael@0 | 880 | void Channel::ChannelImpl::OutputQueuePush(Message* msg) |
michael@0 | 881 | { |
michael@0 | 882 | output_queue_.push(msg); |
michael@0 | 883 | output_queue_length_++; |
michael@0 | 884 | } |
michael@0 | 885 | |
michael@0 | 886 | void Channel::ChannelImpl::OutputQueuePop() |
michael@0 | 887 | { |
michael@0 | 888 | output_queue_.pop(); |
michael@0 | 889 | output_queue_length_--; |
michael@0 | 890 | } |
michael@0 | 891 | |
michael@0 | 892 | // Called by libevent when we can write to the pipe without blocking. |
michael@0 | 893 | void Channel::ChannelImpl::OnFileCanWriteWithoutBlocking(int fd) { |
michael@0 | 894 | if (!ProcessOutgoingMessages()) { |
michael@0 | 895 | Close(); |
michael@0 | 896 | listener_->OnChannelError(); |
michael@0 | 897 | } |
michael@0 | 898 | } |
michael@0 | 899 | |
michael@0 | 900 | void Channel::ChannelImpl::Close() { |
michael@0 | 901 | // Close can be called multiple times, so we need to make sure we're |
michael@0 | 902 | // idempotent. |
michael@0 | 903 | |
michael@0 | 904 | // Unregister libevent for the listening socket and close it. |
michael@0 | 905 | server_listen_connection_watcher_.StopWatchingFileDescriptor(); |
michael@0 | 906 | |
michael@0 | 907 | if (server_listen_pipe_ != -1) { |
michael@0 | 908 | HANDLE_EINTR(close(server_listen_pipe_)); |
michael@0 | 909 | server_listen_pipe_ = -1; |
michael@0 | 910 | } |
michael@0 | 911 | |
michael@0 | 912 | // Unregister libevent for the FIFO and close it. |
michael@0 | 913 | read_watcher_.StopWatchingFileDescriptor(); |
michael@0 | 914 | write_watcher_.StopWatchingFileDescriptor(); |
michael@0 | 915 | if (pipe_ != -1) { |
michael@0 | 916 | HANDLE_EINTR(close(pipe_)); |
michael@0 | 917 | pipe_ = -1; |
michael@0 | 918 | } |
michael@0 | 919 | if (client_pipe_ != -1) { |
michael@0 | 920 | Singleton<PipeMap>()->Remove(pipe_name_); |
michael@0 | 921 | HANDLE_EINTR(close(client_pipe_)); |
michael@0 | 922 | client_pipe_ = -1; |
michael@0 | 923 | } |
michael@0 | 924 | |
michael@0 | 925 | if (uses_fifo_) { |
michael@0 | 926 | // Unlink the FIFO |
michael@0 | 927 | unlink(pipe_name_.c_str()); |
michael@0 | 928 | } |
michael@0 | 929 | |
michael@0 | 930 | while (!output_queue_.empty()) { |
michael@0 | 931 | Message* m = output_queue_.front(); |
michael@0 | 932 | OutputQueuePop(); |
michael@0 | 933 | delete m; |
michael@0 | 934 | } |
michael@0 | 935 | |
michael@0 | 936 | // Close any outstanding, received file descriptors |
michael@0 | 937 | for (std::vector<int>::iterator |
michael@0 | 938 | i = input_overflow_fds_.begin(); i != input_overflow_fds_.end(); ++i) { |
michael@0 | 939 | HANDLE_EINTR(close(*i)); |
michael@0 | 940 | } |
michael@0 | 941 | input_overflow_fds_.clear(); |
michael@0 | 942 | |
michael@0 | 943 | #if defined(OS_MACOSX) |
michael@0 | 944 | for (std::list<PendingDescriptors>::iterator i = pending_fds_.begin(); |
michael@0 | 945 | i != pending_fds_.end(); |
michael@0 | 946 | i++) { |
michael@0 | 947 | (*i).fds->CommitAll(); |
michael@0 | 948 | } |
michael@0 | 949 | pending_fds_.clear(); |
michael@0 | 950 | #endif |
michael@0 | 951 | |
michael@0 | 952 | closed_ = true; |
michael@0 | 953 | } |
michael@0 | 954 | |
michael@0 | 955 | bool Channel::ChannelImpl::Unsound_IsClosed() const |
michael@0 | 956 | { |
michael@0 | 957 | return closed_; |
michael@0 | 958 | } |
michael@0 | 959 | |
michael@0 | 960 | uint32_t Channel::ChannelImpl::Unsound_NumQueuedMessages() const |
michael@0 | 961 | { |
michael@0 | 962 | return output_queue_length_; |
michael@0 | 963 | } |
michael@0 | 964 | |
michael@0 | 965 | //------------------------------------------------------------------------------ |
michael@0 | 966 | // Channel's methods simply call through to ChannelImpl. |
michael@0 | 967 | Channel::Channel(const std::wstring& channel_id, Mode mode, |
michael@0 | 968 | Listener* listener) |
michael@0 | 969 | : channel_impl_(new ChannelImpl(channel_id, mode, listener)) { |
michael@0 | 970 | } |
michael@0 | 971 | |
michael@0 | 972 | Channel::Channel(int fd, Mode mode, Listener* listener) |
michael@0 | 973 | : channel_impl_(new ChannelImpl(fd, mode, listener)) { |
michael@0 | 974 | } |
michael@0 | 975 | |
michael@0 | 976 | Channel::~Channel() { |
michael@0 | 977 | delete channel_impl_; |
michael@0 | 978 | } |
michael@0 | 979 | |
michael@0 | 980 | bool Channel::Connect() { |
michael@0 | 981 | return channel_impl_->Connect(); |
michael@0 | 982 | } |
michael@0 | 983 | |
michael@0 | 984 | void Channel::Close() { |
michael@0 | 985 | channel_impl_->Close(); |
michael@0 | 986 | } |
michael@0 | 987 | |
michael@0 | 988 | Channel::Listener* Channel::set_listener(Listener* listener) { |
michael@0 | 989 | return channel_impl_->set_listener(listener); |
michael@0 | 990 | } |
michael@0 | 991 | |
michael@0 | 992 | bool Channel::Send(Message* message) { |
michael@0 | 993 | return channel_impl_->Send(message); |
michael@0 | 994 | } |
michael@0 | 995 | |
michael@0 | 996 | void Channel::GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const { |
michael@0 | 997 | return channel_impl_->GetClientFileDescriptorMapping(src_fd, dest_fd); |
michael@0 | 998 | } |
michael@0 | 999 | |
michael@0 | 1000 | void Channel::ResetFileDescriptor(int fd) { |
michael@0 | 1001 | channel_impl_->ResetFileDescriptor(fd); |
michael@0 | 1002 | } |
michael@0 | 1003 | |
michael@0 | 1004 | int Channel::GetFileDescriptor() const { |
michael@0 | 1005 | return channel_impl_->GetFileDescriptor(); |
michael@0 | 1006 | } |
michael@0 | 1007 | |
michael@0 | 1008 | void Channel::CloseClientFileDescriptor() { |
michael@0 | 1009 | channel_impl_->CloseClientFileDescriptor(); |
michael@0 | 1010 | } |
michael@0 | 1011 | |
michael@0 | 1012 | bool Channel::Unsound_IsClosed() const { |
michael@0 | 1013 | return channel_impl_->Unsound_IsClosed(); |
michael@0 | 1014 | } |
michael@0 | 1015 | |
michael@0 | 1016 | uint32_t Channel::Unsound_NumQueuedMessages() const { |
michael@0 | 1017 | return channel_impl_->Unsound_NumQueuedMessages(); |
michael@0 | 1018 | } |
michael@0 | 1019 | |
michael@0 | 1020 | } // namespace IPC |