| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (c) 2015 The Native Client Authors. All rights reserved. | 2 * Copyright (c) 2015 The Native Client Authors. All rights reserved. |
| 3 * Use of this source code is governed by a BSD-style license that can be | 3 * Use of this source code is governed by a BSD-style license that can be |
| 4 * found in the LICENSE file. | 4 * found in the LICENSE file. |
| 5 */ | 5 */ |
| 6 | 6 |
| 7 #include <errno.h> | 7 #include <errno.h> |
| 8 #include <stdlib.h> | 8 #include <stdlib.h> |
| 9 #include <string.h> | 9 #include <string.h> |
| 10 | 10 |
| 11 #include <algorithm> | 11 #include <algorithm> |
| 12 #include <string> | 12 #include <string> |
| 13 | 13 |
| 14 #include "native_client/src/include/build_config.h" | 14 #include "native_client/src/include/build_config.h" |
| 15 #include "native_client/src/include/nacl_scoped_ptr.h" | 15 #include "native_client/src/include/nacl_scoped_ptr.h" |
| 16 #include "native_client/src/shared/platform/nacl_check.h" | 16 #include "native_client/src/shared/platform/nacl_check.h" |
| 17 #include "native_client/src/shared/platform/nacl_log.h" | 17 #include "native_client/src/shared/platform/nacl_log.h" |
| 18 #include "native_client/src/trusted/debug_stub/transport.h" | 18 #include "native_client/src/trusted/debug_stub/transport.h" |
| 19 #include "native_client/src/trusted/service_runtime/sel_ldr.h" | 19 #include "native_client/src/trusted/service_runtime/sel_ldr.h" |
| 20 | 20 |
| 21 #if NACL_WINDOWS | |
| 22 #include <windows.h> | |
| 23 #endif | |
| 24 | 21 |
| 25 namespace port { | 22 namespace port { |
| 26 | 23 |
| 27 // This transport is to handle connections over ipc to another | 24 // This transport is to handle connections over ipc to another |
| 28 // process. Instead of directly talking over a server socket, | 25 // process. Instead of directly talking over a server socket, |
| 29 // the server socket is listening on the other process and | 26 // the server socket is listening on the other process and |
| 30 // ferries the data between the socket and the given pipe. | 27 // ferries the data between the socket and the given pipe. |
| 31 // | 28 // |
| 32 // Since we also need to handle multiple sequential connections | 29 // Since we also need to handle multiple sequential connections |
| 33 // but cannot close our handle the incoming data is encoded. | 30 // but cannot close our handle the incoming data is encoded. |
| 34 // The encoding is simple and just prepends a 4 byte length | 31 // The encoding is simple and just prepends a 4 byte length |
| 35 // to each message. A length of -1 signifies that the server | 32 // to each message. A length of -1 signifies that the server |
| 36 // socket was disconnected and is awaiting a new connection. | 33 // socket was disconnected and is awaiting a new connection. |
| 37 // Once in a disconnected state any new incoming data will | 34 // Once in a disconnected state any new incoming data will |
| 38 // symbolize a new connection. | 35 // symbolize a new connection. |
| 36 |
| 37 // TODO(leslieb): implement for windows. |
| 38 #if NACL_LINUX || NACL_OSX |
| 39 class TransportIPC : public ITransport { | 39 class TransportIPC : public ITransport { |
| 40 public: | 40 public: |
| 41 TransportIPC() | 41 TransportIPC() |
| 42 : buf_(new char[kBufSize]), | 42 : buf_(new char[kBufSize]), |
| 43 unconsumed_bytes_(0), | 43 unconsumed_bytes_(0), |
| 44 bytes_to_read_(kServerSocketDisconnect), | 44 bytes_to_read_(kServerSocketDisconnect), |
| 45 handle_(NACL_INVALID_HANDLE) { } | 45 handle_(NACL_INVALID_HANDLE) { } |
| 46 | 46 |
| 47 explicit TransportIPC(NaClHandle fd) | 47 explicit TransportIPC(NaClHandle fd) |
| 48 : buf_(new char[kBufSize]), | 48 : buf_(new char[kBufSize]), |
| 49 unconsumed_bytes_(0), | 49 unconsumed_bytes_(0), |
| 50 bytes_to_read_(kServerSocketDisconnect), | 50 bytes_to_read_(kServerSocketDisconnect), |
| 51 handle_(fd) { } | 51 handle_(fd) { } |
| 52 | 52 |
| 53 ~TransportIPC() { | 53 ~TransportIPC() { |
| 54 if (handle_ != NACL_INVALID_HANDLE) { | 54 if (handle_ != NACL_INVALID_HANDLE) { |
| 55 #if NACL_WINDOWS | |
| 56 if (!CloseHandle(handle_)) | |
| 57 #else | |
| 58 if (::close(handle_)) | 55 if (::close(handle_)) |
| 59 #endif | |
| 60 NaClLog(LOG_FATAL, | 56 NaClLog(LOG_FATAL, |
| 61 "TransportIPC::Disconnect: Failed to close handle.\n"); | 57 "TransportIPC::Disconnect: Failed to close handle.\n"); |
| 62 } | 58 } |
| 63 } | 59 } |
| 64 | 60 |
| 65 // Read from this transport, return true on success. | 61 // Read from this transport, return true on success. |
| 66 // Returning false means we have disconnected on the server end. | 62 // Returning false means we have disconnected on the server end. |
| 67 virtual bool Read(void *ptr, int32_t len) { | 63 virtual bool Read(void *ptr, int32_t len) { |
| 68 CHECK(IsConnected()); | 64 CHECK(IsConnected()); |
| 69 CHECK(ptr && len >= 0); | 65 CHECK(ptr && len >= 0); |
| 70 char *dst = static_cast<char *>(ptr); | 66 char *dst = static_cast<char *>(ptr); |
| 71 | 67 |
| 72 while (len > 0) { | 68 while (len > 0) { |
| 73 if (!FillBufferIfEmpty()) return false; | 69 if (!FillBufferIfEmpty()) return false; |
| 74 CopyFromBuffer(&dst, &len); | 70 CopyFromBuffer(&dst, &len); |
| 75 } | 71 } |
| 76 return true; | 72 return true; |
| 77 } | 73 } |
| 78 | 74 |
| 79 // Write to this transport, return true on success. | 75 // Write to this transport, return true on success. |
| 80 virtual bool Write(const void *ptr, int32_t len) { | 76 virtual bool Write(const void *ptr, int32_t len) { |
| 81 CHECK(IsConnected()); | 77 CHECK(IsConnected()); |
| 82 CHECK(ptr && len >= 0); | 78 CHECK(ptr && len >= 0); |
| 83 const char *src = static_cast<const char *>(ptr); | 79 const char *src = static_cast<const char *>(ptr); |
| 84 while (len > 0) { | 80 while (len > 0) { |
| 85 int result = WriteInternal(handle_, src, len); | 81 int result = ::write(handle_, src, len); |
| 86 if (result >= 0) { | 82 if (result > 0) { |
| 87 src += result; | 83 src += result; |
| 88 len -= result; | 84 len -= result; |
| 89 } else { | 85 } else if (result == 0 || errno != EINTR) { |
| 90 NaClLog(LOG_FATAL, | 86 NaClLog(LOG_FATAL, |
| 91 "TransportIPC::Write: Pipe closed from other process.\n"); | 87 "TransportIPC::Write: Pipe closed from other process.\n"); |
| 92 } | 88 } |
| 93 } | 89 } |
| 94 return true; | 90 return true; |
| 95 } | 91 } |
| 96 | 92 |
| 97 // Return true if there is data to read. | 93 // Return true if there is data to read. |
| 98 virtual bool IsDataAvailable() { | 94 virtual bool IsDataAvailable() { |
| 99 CHECK(IsConnected()); | 95 CHECK(IsConnected()); |
| 100 if (unconsumed_bytes_ > 0) return true; | 96 if (unconsumed_bytes_ > 0) return true; |
| 101 | |
| 102 #if NACL_WINDOWS | |
| 103 DWORD available_bytes = 0; | |
| 104 // Return true if the pipe has data or there is an error. | |
| 105 if (PeekNamedPipe(handle_, NULL, 0, NULL, &available_bytes, NULL)) | |
| 106 return available_bytes > 0; | |
| 107 else | |
| 108 return true; | |
| 109 #else | |
| 110 fd_set fds; | 97 fd_set fds; |
| 111 | 98 |
| 112 FD_ZERO(&fds); | 99 FD_ZERO(&fds); |
| 113 FD_SET(handle_, &fds); | 100 FD_SET(handle_, &fds); |
| 114 | 101 |
| 115 // We want a "non-blocking" check | 102 // We want a "non-blocking" check |
| 116 struct timeval timeout; | 103 struct timeval timeout; |
| 117 timeout.tv_sec = 0; | 104 timeout.tv_sec = 0; |
| 118 timeout.tv_usec = 0; | 105 timeout.tv_usec = 0; |
| 119 | 106 |
| 120 // Check if this file handle can select on read | 107 // Check if this file handle can select on read |
| 121 int cnt = select(handle_ + 1, &fds, 0, 0, &timeout); | 108 int cnt = select(handle_ + 1, &fds, 0, 0, &timeout); |
| 122 | 109 |
| 123 // If we are ready, or if there is an error. We return true | 110 // If we are ready, or if there is an error. We return true |
| 124 // on error, to let the next IO request fail. | 111 // on error, to let the next IO request fail. |
| 125 if (cnt != 0) return true; | 112 if (cnt != 0) return true; |
| 126 | 113 |
| 127 return false; | 114 return false; |
| 128 #endif | |
| 129 } | 115 } |
| 130 | 116 |
| 131 void WaitForDebugStubEvent(struct NaClApp *nap, | 117 void WaitForDebugStubEvent(struct NaClApp *nap, |
| 132 bool ignore_input_from_gdb) { | 118 bool ignore_input_from_gdb) { |
| 133 bool wait = true; | 119 bool wait = true; |
| 134 // If we are told to ignore messages from gdb, we will exit from this | 120 // If we are told to ignore messages from gdb, we will exit from this |
| 135 // function only if new data is sent by gdb. | 121 // function only if new data is sent by gdb. |
| 136 if ((unconsumed_bytes_ > 0 && !ignore_input_from_gdb) || | 122 if ((unconsumed_bytes_ > 0 && !ignore_input_from_gdb) || |
| 137 nap->faulted_thread_count > 0) { | 123 nap->faulted_thread_count > 0) { |
| 138 // Clear faulted thread events to save debug stub loop iterations. | 124 // Clear faulted thread events to save debug stub loop iterations. |
| 139 wait = false; | 125 wait = false; |
| 140 } | 126 } |
| 141 | 127 |
| 142 #if NACL_WINDOWS | |
| 143 HANDLE handles[2]; | |
| 144 handles[0] = nap->faulted_thread_event; | |
| 145 handles[1] = handle_; | |
| 146 int result = WaitForMultipleObjects(2, handles, FALSE, | |
| 147 wait ? INFINITE : 0); | |
| 148 if (result == WAIT_OBJECT_0 || | |
| 149 result == WAIT_OBJECT_0 + 1 || | |
| 150 result == WAIT_TIMEOUT) | |
| 151 return; | |
| 152 NaClLog(LOG_FATAL, | |
| 153 "TransportIPC::WaitForDebugStubEvent: Wait for events failed.\n"); | |
| 154 #else | |
| 155 fd_set fds; | 128 fd_set fds; |
| 156 | 129 |
| 157 FD_ZERO(&fds); | 130 FD_ZERO(&fds); |
| 158 FD_SET(nap->faulted_thread_fd_read, &fds); | 131 FD_SET(nap->faulted_thread_fd_read, &fds); |
| 159 int max_fd = nap->faulted_thread_fd_read; | 132 int max_fd = nap->faulted_thread_fd_read; |
| 160 if (unconsumed_bytes_ < kBufSize) { | 133 if (unconsumed_bytes_ < kBufSize) { |
| 161 FD_SET(handle_, &fds); | 134 FD_SET(handle_, &fds); |
| 162 max_fd = std::max(max_fd, handle_); | 135 max_fd = std::max(max_fd, handle_); |
| 163 } | 136 } |
| 164 | 137 |
| 165 int ret; | 138 int ret; |
| 166 // We don't need sleep-polling on Linux now, | 139 // We don't need sleep-polling on Linux now, |
| 167 // so we set either zero or infinite timeout. | 140 // so we set either zero or infinite timeout. |
| 168 if (wait) { | 141 if (wait) { |
| 169 ret = select(max_fd + 1, &fds, NULL, NULL, NULL); | 142 ret = select(max_fd + 1, &fds, NULL, NULL, NULL); |
| 170 } else { | 143 } else { |
| 171 struct timeval timeout; | 144 struct timeval timeout; |
| 172 timeout.tv_sec = 0; | 145 timeout.tv_sec = 0; |
| 173 timeout.tv_usec = 0; | 146 timeout.tv_usec = 0; |
| 174 ret = select(max_fd + 1, &fds, NULL, NULL, &timeout); | 147 ret = select(max_fd + 1, &fds, NULL, NULL, &timeout); |
| 175 } | 148 } |
| 176 if (ret < 0) { | 149 if (ret < 0) { |
| 177 NaClLog(LOG_FATAL, | 150 NaClLog(LOG_FATAL, |
| 178 "TransportIPC::WaitForDebugStubEvent: Wait for events failed.\n"); | 151 "TransportIPC::WaitForDebugStubEvent: Failed to wait for " |
| 152 "debug stub event.\n"); |
| 179 } | 153 } |
| 180 | 154 |
| 181 if (ret > 0) { | 155 if (ret > 0) { |
| 182 if (FD_ISSET(nap->faulted_thread_fd_read, &fds)) { | 156 if (FD_ISSET(nap->faulted_thread_fd_read, &fds)) { |
| 183 char buf[16]; | 157 char buf[16]; |
| 184 if (read(nap->faulted_thread_fd_read, &buf, sizeof(buf)) < 0) { | 158 if (read(nap->faulted_thread_fd_read, &buf, sizeof(buf)) < 0) { |
| 185 NaClLog(LOG_FATAL, | 159 NaClLog(LOG_FATAL, |
| 186 "TransportIPC::WaitForDebugStubEvent: Failed to read from " | 160 "TransportIPC::WaitForDebugStubEvent: Failed to read from " |
| 187 "debug stub event pipe fd.\n"); | 161 "debug stub event pipe fd.\n"); |
| 188 } | 162 } |
| 189 } | 163 } |
| 190 if (FD_ISSET(handle_, &fds)) | 164 if (FD_ISSET(handle_, &fds)) |
| 191 FillBufferIfEmpty(); | 165 FillBufferIfEmpty(); |
| 192 } | 166 } |
| 193 #endif | |
| 194 } | 167 } |
| 195 | 168 |
| 196 virtual void Disconnect() { | 169 virtual void Disconnect() { |
| 197 // If we are being marked as disconnected then we should also | 170 // If we are being marked as disconnected then we should also |
| 198 // receive the disconnect marker so the next connection is in | 171 // receive the disconnect marker so the next connection is in |
| 199 // a proper state. | 172 // a proper state. |
| 200 if (IsConnected()) { | 173 if (IsConnected()) { |
| 201 do { | 174 do { |
| 202 unconsumed_bytes_ = 0; | 175 unconsumed_bytes_ = 0; |
| 203 // FillBufferIfEmpty() returns false on disconnect or a | 176 // FillBufferIfEmpty() returns false on disconnect or a |
| 204 // Fatal error, and in both cases we want to exit the loop. | 177 // Fatal error, and in both cases we want to exit the loop. |
| 205 } while (FillBufferIfEmpty()); | 178 } while (FillBufferIfEmpty()); |
| 206 } | 179 } |
| 207 | 180 |
| 208 // Throw away unused data. | 181 // Throw away unused data. |
| 209 unconsumed_bytes_ = 0; | 182 unconsumed_bytes_ = 0; |
| 210 } | 183 } |
| 211 | 184 |
| 212 // Block until we have new data on the pipe, new data means the | 185 // Block until we have new data on the pipe, new data means the |
| 213 // server socket across the pipe got a new connection. | 186 // server socket across the pipe got a new connection. |
| 214 virtual bool AcceptConnection() { | 187 virtual bool AcceptConnection() { |
| 215 CHECK(!IsConnected()); | 188 CHECK(!IsConnected()); |
| 216 #if NACL_WINDOWS | |
| 217 if (WaitForSingleObject(handle_, INFINITE) == WAIT_OBJECT_0) { | |
| 218 // This marks ourself as connected. | |
| 219 bytes_to_read_ = 0; | |
| 220 return true; | |
| 221 } | |
| 222 NaClLog(LOG_FATAL, | |
| 223 "TransportIPC::WaitForDebugStubEvent: Wait for events failed.\n"); | |
| 224 return false; | |
| 225 #else | |
| 226 fd_set fds; | 189 fd_set fds; |
| 227 | 190 |
| 228 FD_ZERO(&fds); | 191 FD_ZERO(&fds); |
| 229 FD_SET(handle_, &fds); | 192 FD_SET(handle_, &fds); |
| 230 | 193 |
| 231 // Check if this file handle can select on read | 194 // Check if this file handle can select on read |
| 232 int cnt = select(handle_ + 1, &fds, 0, 0, NULL); | 195 int cnt = select(handle_ + 1, &fds, 0, 0, NULL); |
| 233 | 196 |
| 234 // If we are ready, or if there is an error. We return true | 197 // If we are ready, or if there is an error. We return true |
| 235 // on error, to let the next IO request fail. | 198 // on error, to let the next IO request fail. |
| 236 if (cnt != 0) { | 199 if (cnt != 0) { |
| 237 // This marks ourself as connected. | 200 // This marks ourself as connected. |
| 238 bytes_to_read_ = 0; | 201 bytes_to_read_ = 0; |
| 239 return true; | 202 return true; |
| 240 } | 203 } |
| 241 | 204 |
| 242 return false; | 205 return false; |
| 243 #endif | |
| 244 } | 206 } |
| 245 | 207 |
| 246 private: | 208 private: |
| 247 // Returns whether we are in a connected state. This refers to the | 209 // Returns whether we are in a connected state. This refers to the |
| 248 // connection of the server across the pipe, if the pipe itself is | 210 // connection of the server across the pipe, if the pipe itself is |
| 249 // ever disconnected we will probably be crashing or exitting soon. | 211 // ever disconnected we will probably be crashing or exitting soon. |
| 250 bool IsConnected() { | 212 bool IsConnected() { |
| 251 return bytes_to_read_ != kServerSocketDisconnect; | 213 return bytes_to_read_ != kServerSocketDisconnect; |
| 252 } | 214 } |
| 253 | 215 |
| (...skipping 28 matching lines...) Expand all Loading... |
| 282 // We want to mark ourselves as disconnected on an error. | 244 // We want to mark ourselves as disconnected on an error. |
| 283 bytes_to_read_ = kServerSocketDisconnect; | 245 bytes_to_read_ = kServerSocketDisconnect; |
| 284 return false; | 246 return false; |
| 285 } | 247 } |
| 286 | 248 |
| 287 // If we got the disconnect flag mark it as such. | 249 // If we got the disconnect flag mark it as such. |
| 288 if (bytes_to_read_ == kServerSocketDisconnect) | 250 if (bytes_to_read_ == kServerSocketDisconnect) |
| 289 return false; | 251 return false; |
| 290 } | 252 } |
| 291 | 253 |
| 292 int result = ReadInternal(handle_, buf_.get() + unconsumed_bytes_, | 254 int result = ::read(handle_, buf_.get() + unconsumed_bytes_, |
| 293 std::min(bytes_to_read_, kBufSize)); | 255 std::min(bytes_to_read_, kBufSize)); |
| 294 if (result >= 0) { | 256 if (result > 0) { |
| 295 unconsumed_bytes_ += result; | 257 unconsumed_bytes_ += result; |
| 296 bytes_to_read_ -= result; | 258 bytes_to_read_ -= result; |
| 297 } else { | 259 } else if (result == 0 || errno != EINTR) { |
| 298 NaClLog(LOG_FATAL, | 260 NaClLog(LOG_FATAL, |
| 299 "TransportIPC::FillBufferIfEmpty: " | 261 "TransportIPC::FillBufferIfEmpty: " |
| 300 "Pipe closed from other process.\n"); | 262 "Pipe closed from other process.\n"); |
| 301 // We want to mark ourselves as disconnected on an error. | 263 // We want to mark ourselves as disconnected on an error. |
| 302 bytes_to_read_ = kServerSocketDisconnect; | 264 bytes_to_read_ = kServerSocketDisconnect; |
| 303 return false; | 265 return false; |
| 304 } | 266 } |
| 305 | 267 |
| 306 return true; | 268 return true; |
| 307 } | 269 } |
| 308 | 270 |
| 309 // Block until you read len bytes. Return false on error. | 271 // Block until you read len bytes. |
| 272 // Return false on EOF or error, but retries EINTR. |
| 310 bool ReadNBytes(char *buf, uint32_t len) { | 273 bool ReadNBytes(char *buf, uint32_t len) { |
| 311 uint32_t bytes_read = 0; | 274 uint32_t bytes_read = 0; |
| 312 while (len > 0) { | 275 while (len > 0) { |
| 313 int result = ReadInternal(handle_, buf + bytes_read, len); | 276 int result = ::read(handle_, buf + bytes_read, len); |
| 314 if (result >= 0) { | 277 if (result > 0) { |
| 315 bytes_read += result; | 278 bytes_read += result; |
| 316 len -= result; | 279 len -= result; |
| 317 } else { | 280 } else if (result == 0 || errno != EINTR) { |
| 318 return false; | 281 return false; |
| 319 } | 282 } |
| 320 } | 283 } |
| 321 return true; | 284 return true; |
| 322 } | 285 } |
| 323 | 286 |
| 324 // Platform independant read. Returns number of bytes read or | |
| 325 // -1 on eof or error. Eof is treated as an error since the pipe | |
| 326 // should never be closed on the other end. On posix EINTR will | |
| 327 // return 0. | |
| 328 static int ReadInternal(NaClHandle handle, char *buf, int len) { | |
| 329 #if NACL_WINDOWS | |
| 330 DWORD bytes_read = 0; | |
| 331 | |
| 332 if (!ReadFile(handle, buf, len, &bytes_read, NULL)) | |
| 333 return -1; | |
| 334 else | |
| 335 return static_cast<int>(bytes_read); | |
| 336 #else | |
| 337 int result = ::read(handle, buf, len); | |
| 338 if (result > 0) | |
| 339 return result; | |
| 340 else if (result == 0 || errno != EINTR) | |
| 341 return -1; | |
| 342 else | |
| 343 return 0; | |
| 344 #endif | |
| 345 } | |
| 346 | |
| 347 // Platform independant write. Returns number of bytes read or | |
| 348 // -1 on eof or error. Eof is treated as an error since the pipe | |
| 349 // should never be closed on the other end. On posix EINTR will | |
| 350 // return 0. | |
| 351 static int WriteInternal(NaClHandle handle, const char *buf, int len) { | |
| 352 #if NACL_WINDOWS | |
| 353 DWORD bytes_written = 0; | |
| 354 | |
| 355 if (!WriteFile(handle, buf, len, &bytes_written, NULL)) | |
| 356 return -1; | |
| 357 else | |
| 358 return static_cast<int>(bytes_written); | |
| 359 #else | |
| 360 int result = ::write(handle, buf, len); | |
| 361 if (result > 0) | |
| 362 return result; | |
| 363 else if (result == 0 || errno != EINTR) | |
| 364 return -1; | |
| 365 else | |
| 366 return 0; | |
| 367 #endif | |
| 368 } | |
| 369 | |
| 370 static const int kServerSocketDisconnect = -1; | 287 static const int kServerSocketDisconnect = -1; |
| 371 static const int kBufSize = 4096; | 288 static const int kBufSize = 4096; |
| 372 nacl::scoped_array<char> buf_; | 289 nacl::scoped_array<char> buf_; |
| 373 | 290 |
| 374 // Number of bytes stored in internal buffer. | 291 // Number of bytes stored in internal buffer. |
| 375 int32_t unconsumed_bytes_; | 292 int32_t unconsumed_bytes_; |
| 376 | 293 |
| 377 // Number of bytes left in packet encoding from browser. | 294 // Number of bytes left in packet encoding from browser. |
| 378 int32_t bytes_to_read_; | 295 int32_t bytes_to_read_; |
| 379 NaClHandle handle_; | 296 NaClHandle handle_; |
| 380 }; | 297 }; |
| 381 | 298 |
| 382 // The constant is passed by reference in some cases. So | 299 // The constant is passed by reference in some cases. So |
| 383 // under some optmizations or lack thereof it needs space. | 300 // under some optmizations or lack thereof it needs space. |
| 384 const int TransportIPC::kBufSize; | 301 const int TransportIPC::kBufSize; |
| 385 | 302 |
| 303 #endif |
| 304 |
| 386 ITransport *CreateTransportIPC(NaClHandle fd) { | 305 ITransport *CreateTransportIPC(NaClHandle fd) { |
| 306 #if NACL_LINUX || NACL_OSX |
| 387 return new TransportIPC(fd); | 307 return new TransportIPC(fd); |
| 308 #else |
| 309 // TODO(leslieb): implement for windows. |
| 310 return NULL; |
| 311 #endif |
| 388 } | 312 } |
| 389 | 313 |
| 390 } // namespace port | 314 } // namespace port |
| OLD | NEW |