OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ipc/ipc_channel_nacl.h" | 5 #include "ipc/ipc_channel_nacl.h" |
6 | 6 |
| 7 #include <errno.h> |
| 8 #include <stddef.h> |
| 9 #include <sys/nacl_imc_api.h> |
| 10 #include <sys/nacl_syscalls.h> |
| 11 #include <sys/types.h> |
| 12 |
| 13 #include <algorithm> |
| 14 |
| 15 #include "base/bind.h" |
7 #include "base/file_util.h" | 16 #include "base/file_util.h" |
8 #include "base/logging.h" | 17 #include "base/logging.h" |
9 | 18 #include "base/message_loop_proxy.h" |
10 // This file is currently a stub to get us linking. | 19 #include "base/process_util.h" |
11 // TODO(brettw) implement this. | 20 #include "base/synchronization/lock.h" |
| 21 #include "base/task_runner_util.h" |
| 22 #include "base/threading/simple_thread.h" |
| 23 #include "ipc/file_descriptor_set_posix.h" |
| 24 #include "ipc/ipc_logging.h" |
12 | 25 |
13 namespace IPC { | 26 namespace IPC { |
| 27 namespace { |
| 28 |
| 29 scoped_ptr<std::vector<char> > ReadDataOnReaderThread(int pipe) { |
| 30 DCHECK(pipe >= 0); |
| 31 |
| 32 if (pipe < 0) |
| 33 return scoped_ptr<std::vector<char> >(); |
| 34 |
| 35 scoped_ptr<std::vector<char> > buffer( |
| 36 new std::vector<char>(Channel::kReadBufferSize)); |
| 37 struct NaClImcMsgHdr msg = {0}; |
| 38 struct NaClImcMsgIoVec iov = {&buffer->at(0), buffer->size()}; |
| 39 msg.iov = &iov; |
| 40 msg.iov_length = 1; |
| 41 |
| 42 int bytes_read = imc_recvmsg(pipe, &msg, 0); |
| 43 |
| 44 if (bytes_read <= 0) { |
| 45 // NaClIPCAdapter::BlockingReceive returns -1 when the pipe closes (either |
| 46 // due to error or for regular shutdown). |
| 47 return scoped_ptr<std::vector<char> >(); |
| 48 } |
| 49 DCHECK(bytes_read); |
| 50 buffer->resize(bytes_read); |
| 51 return buffer.Pass(); |
| 52 } |
| 53 |
| 54 } // namespace |
| 55 |
| 56 class Channel::ChannelImpl::ReaderThreadRunner |
| 57 : public base::DelegateSimpleThread::Delegate { |
| 58 public: |
| 59 // |pipe|: A file descriptor from which we will read using imc_recvmsg. |
| 60 // |data_read_callback|: A callback we invoke (on the main thread) when we |
| 61 // have read data. The callback is passed a buffer of |
| 62 // data that was read. |
| 63 // |failure_callback|: A callback we invoke when we have a failure reading |
| 64 // from |pipe|. |
| 65 // |main_message_loop|: A proxy for the main thread, where we will invoke the |
| 66 // above callbacks. |
| 67 ReaderThreadRunner( |
| 68 int pipe, |
| 69 base::Callback<void (scoped_ptr<std::vector<char> >)> data_read_callback, |
| 70 base::Callback<void ()> failure_callback, |
| 71 base::MessageLoopProxy* main_message_loop); |
| 72 |
| 73 // DelegateSimpleThread implementation. Reads data from the pipe in a loop |
| 74 // until either we are told to quit or a read fails. |
| 75 virtual void Run() OVERRIDE; |
| 76 |
| 77 private: |
| 78 int pipe_; |
| 79 base::Callback<void (scoped_ptr<std::vector<char> >)> data_read_callback_; |
| 80 base::Callback<void ()> failure_callback_; |
| 81 base::MessageLoopProxy* main_message_loop_; |
| 82 |
| 83 DISALLOW_COPY_AND_ASSIGN(ReaderThreadRunner); |
| 84 }; |
| 85 |
| 86 Channel::ChannelImpl::ReaderThreadRunner::ReaderThreadRunner( |
| 87 int pipe, |
| 88 base::Callback<void (scoped_ptr<std::vector<char> >)> data_read_callback, |
| 89 base::Callback<void ()> failure_callback, |
| 90 base::MessageLoopProxy* main_message_loop) |
| 91 : pipe_(pipe), |
| 92 data_read_callback_(data_read_callback), |
| 93 failure_callback_(failure_callback), |
| 94 main_message_loop_(main_message_loop) { |
| 95 } |
| 96 |
| 97 void Channel::ChannelImpl::ReaderThreadRunner::Run() { |
| 98 while (true) { |
| 99 scoped_ptr<std::vector<char> > buffer(ReadDataOnReaderThread(pipe_)); |
| 100 if (buffer.get()) { |
| 101 main_message_loop_->PostTask(FROM_HERE, |
| 102 base::Bind(data_read_callback_, base::Passed(buffer.Pass()))); |
| 103 } else { |
| 104 main_message_loop_->PostTask(FROM_HERE, failure_callback_); |
| 105 // Because the read failed, we know we're going to quit. Don't bother |
| 106 // trying to read again. |
| 107 return; |
| 108 } |
| 109 } |
| 110 } |
14 | 111 |
15 Channel::ChannelImpl::ChannelImpl(const IPC::ChannelHandle& channel_handle, | 112 Channel::ChannelImpl::ChannelImpl(const IPC::ChannelHandle& channel_handle, |
16 Mode mode, | 113 Mode mode, |
17 Listener* listener) | 114 Listener* listener) |
18 : ChannelReader(listener) { | 115 : ChannelReader(listener), |
| 116 mode_(mode), |
| 117 waiting_connect_(true), |
| 118 pipe_(-1), |
| 119 pipe_name_(channel_handle.name), |
| 120 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { |
| 121 if (!CreatePipe(channel_handle)) { |
| 122 // The pipe may have been closed already. |
| 123 const char *modestr = (mode_ & MODE_SERVER_FLAG) ? "server" : "client"; |
| 124 LOG(WARNING) << "Unable to create pipe named \"" << channel_handle.name |
| 125 << "\" in " << modestr << " mode"; |
| 126 } |
| 127 reader_thread_runner_.reset( |
| 128 new ReaderThreadRunner( |
| 129 pipe_, |
| 130 base::Bind(&Channel::ChannelImpl::DidRecvMsg, |
| 131 weak_ptr_factory_.GetWeakPtr()), |
| 132 base::Bind(&Channel::ChannelImpl::ReadDidFail, |
| 133 weak_ptr_factory_.GetWeakPtr()), |
| 134 base::MessageLoopProxy::current())); |
| 135 reader_thread_.reset( |
| 136 new base::DelegateSimpleThread(reader_thread_runner_.get(), |
| 137 "ipc_channel_nacl reader thread")); |
19 } | 138 } |
20 | 139 |
21 Channel::ChannelImpl::~ChannelImpl() { | 140 Channel::ChannelImpl::~ChannelImpl() { |
22 Close(); | 141 Close(); |
23 } | 142 } |
24 | 143 |
25 bool Channel::ChannelImpl::Connect() { | 144 bool Channel::ChannelImpl::Connect() { |
26 NOTIMPLEMENTED(); | 145 if (pipe_ == -1) { |
27 return false; | 146 DLOG(INFO) << "Channel creation failed: " << pipe_name_; |
| 147 return false; |
| 148 } |
| 149 |
| 150 reader_thread_->Start(); |
| 151 waiting_connect_ = false; |
| 152 // If there were any messages queued before connection, send them. |
| 153 ProcessOutgoingMessages(); |
28 } | 154 } |
29 | 155 |
30 void Channel::ChannelImpl::Close() { | 156 void Channel::ChannelImpl::Close() { |
31 NOTIMPLEMENTED(); | 157 // For now, we assume that at shutdown, the reader thread will be woken with |
| 158 // a failure (see NaClIPCAdapter::BlockingRead and CloseChannel). Or... we |
| 159 // might simply be killed with no chance to clean up anyway :-). |
| 160 // If untrusted code tries to close the channel prior to shutdown, it's likely |
| 161 // to hang. |
| 162 // TODO(dmichael): Can we do anything smarter here to make sure the reader |
| 163 // thread wakes up and quits? |
| 164 reader_thread_->Join(); |
| 165 close(pipe_); |
| 166 pipe_ = -1; |
| 167 reader_thread_runner_.reset(); |
| 168 reader_thread_.reset(); |
| 169 read_queue_.clear(); |
| 170 output_queue_.clear(); |
32 } | 171 } |
33 | 172 |
34 bool Channel::ChannelImpl::Send(Message* message) { | 173 bool Channel::ChannelImpl::Send(Message* message) { |
35 NOTIMPLEMENTED(); | 174 DVLOG(2) << "sending message @" << message << " on channel @" << this |
| 175 << " with type " << message->type(); |
| 176 scoped_ptr<Message> message_ptr(message); |
| 177 |
| 178 #ifdef IPC_MESSAGE_LOG_ENABLED |
| 179 Logging::GetInstance()->OnSendMessage(message, ""); |
| 180 #endif // IPC_MESSAGE_LOG_ENABLED |
| 181 |
| 182 output_queue_.push_back(linked_ptr<Message>(message)); |
| 183 if (!waiting_connect_) |
| 184 return ProcessOutgoingMessages(); |
| 185 |
| 186 return true; |
36 } | 187 } |
37 | 188 |
38 int Channel::ChannelImpl::GetClientFileDescriptor() const { | 189 void Channel::ChannelImpl::DidRecvMsg(scoped_ptr<std::vector<char> > buffer) { |
39 NOTIMPLEMENTED(); | 190 // Close sets the pipe to -1. It's possible we'll get a buffer sent to us from |
40 return -1; | 191 // the reader thread after Close is called. If so, we ignore it. |
| 192 if (pipe_ == -1) |
| 193 return; |
| 194 |
| 195 read_queue_.push_back(linked_ptr<std::vector<char> >(buffer.release())); |
41 } | 196 } |
42 | 197 |
43 int Channel::ChannelImpl::TakeClientFileDescriptor() { | 198 void Channel::ChannelImpl::ReadDidFail() { |
44 NOTIMPLEMENTED(); | 199 Close(); |
45 return -1; | |
46 } | 200 } |
47 | 201 |
48 bool Channel::ChannelImpl::AcceptsConnections() const { | 202 bool Channel::ChannelImpl::CreatePipe( |
49 NOTIMPLEMENTED(); | 203 const IPC::ChannelHandle& channel_handle) { |
50 return false; | 204 DCHECK(pipe_ == -1); |
| 205 |
| 206 // There's one possible case in NaCl: |
| 207 // 1) It's a channel wrapping a pipe that is given to us. |
| 208 // We don't support these: |
| 209 // 2) It's for a named channel. |
| 210 // 3) It's for a client that we implement ourself. |
| 211 // 4) It's the initial IPC channel. |
| 212 |
| 213 if (channel_handle.socket.fd == -1) { |
| 214 NOTIMPLEMENTED(); |
| 215 return false; |
| 216 } |
| 217 pipe_ = channel_handle.socket.fd; |
| 218 return true; |
51 } | 219 } |
52 | 220 |
53 bool Channel::ChannelImpl::HasAcceptedConnection() const { | 221 bool Channel::ChannelImpl::ProcessOutgoingMessages() { |
54 NOTIMPLEMENTED(); | 222 DCHECK(!waiting_connect_); // Why are we trying to send messages if there's |
55 return false; | 223 // no connection? |
| 224 if (output_queue_.empty()) |
| 225 return true; |
| 226 |
| 227 if (pipe_ == -1) |
| 228 return false; |
| 229 |
| 230 // Write out all the messages. The trusted implementation is guaranteed to not |
| 231 // block. See NaClIPCAdapter::Send for the implementation of imc_sendmsg. |
| 232 while (!output_queue_.empty()) { |
| 233 linked_ptr<Message> msg = output_queue_.front(); |
| 234 output_queue_.pop_front(); |
| 235 |
| 236 struct NaClImcMsgHdr msgh = {0}; |
| 237 struct NaClImcMsgIoVec iov = {const_cast<void*>(msg->data()), msg->size()}; |
| 238 msgh.iov = &iov; |
| 239 msgh.iov_length = 1; |
| 240 ssize_t bytes_written = imc_sendmsg(pipe_, &msgh, 0); |
| 241 |
| 242 if (bytes_written < 0) { |
| 243 // The trusted side should only ever give us an error of EPIPE. We |
| 244 // should never be interrupted, nor should we get EAGAIN. |
| 245 DCHECK(errno == EPIPE); |
| 246 Close(); |
| 247 PLOG(ERROR) << "pipe_ error on " |
| 248 << pipe_ |
| 249 << " Currently writing message of size: " |
| 250 << msg->size(); |
| 251 return false; |
| 252 } |
| 253 |
| 254 // Message sent OK! |
| 255 DVLOG(2) << "sent message @" << msg.get() << " with type " << msg->type() |
| 256 << " on fd " << pipe_; |
| 257 } |
| 258 return true; |
56 } | 259 } |
57 | 260 |
58 bool Channel::ChannelImpl::GetClientEuid(uid_t* client_euid) const { | 261 Channel::ChannelImpl::ReadState Channel::ChannelImpl::ReadData( |
59 NOTIMPLEMENTED(); | 262 char* buffer, |
60 return false; | 263 int buffer_len, |
61 } | 264 int* bytes_read) { |
62 | 265 *bytes_read = 0; |
63 void Channel::ChannelImpl::ResetToAcceptingConnectionState() { | 266 if (pipe_ == -1) |
64 NOTIMPLEMENTED(); | 267 return READ_FAILED; |
65 } | 268 if (read_queue_.empty()) |
66 | 269 return READ_PENDING; |
67 Channel::ChannelImpl::ReadState | 270 while (!read_queue_.empty() && *bytes_read < buffer_len) { |
68 Channel::ChannelImpl::ReadData(char* buffer, | 271 linked_ptr<std::vector<char> > vec(read_queue_.front()); |
69 int buffer_len, | 272 int bytes_to_read = buffer_len - *bytes_read; |
70 int* bytes_read) { | 273 if (vec->size() <= bytes_to_read) { |
71 return Channel::ChannelImpl::ReadState(); | 274 // We can read and discard the entire vector. |
| 275 std::copy(vec->begin(), vec->end(), buffer + *bytes_read); |
| 276 *bytes_read += vec->size(); |
| 277 read_queue_.pop_front(); |
| 278 } else { |
| 279 // Read all the bytes we can and discard them from the front of the |
| 280 // vector. (This can be slowish, since erase has to move the back of the |
| 281 // vector to the front, but it's hopefully a temporary hack and it keeps |
| 282 // the code simple). |
| 283 std::copy(vec->begin(), vec->begin() + bytes_to_read, |
| 284 buffer + *bytes_read); |
| 285 vec->erase(vec->begin(), vec->begin() + bytes_to_read); |
| 286 *bytes_read += bytes_to_read; |
| 287 } |
| 288 } |
| 289 return READ_SUCCEEDED; |
72 } | 290 } |
73 | 291 |
74 bool Channel::ChannelImpl::WillDispatchInputMessage(Message* msg) { | 292 bool Channel::ChannelImpl::WillDispatchInputMessage(Message* msg) { |
75 return false; | 293 return true; |
76 } | 294 } |
77 | 295 |
78 bool Channel::ChannelImpl::DidEmptyInputBuffers() { | 296 bool Channel::ChannelImpl::DidEmptyInputBuffers() { |
79 return false; | 297 return true; |
80 } | 298 } |
81 | 299 |
82 void Channel::ChannelImpl::HandleHelloMessage(const Message& msg) { | 300 void Channel::ChannelImpl::HandleHelloMessage(const Message& msg) { |
83 } | 301 // The trusted side IPC::Channel should handle the "hello" handshake; we |
84 | 302 // should not receive the "Hello" message. |
85 // static | 303 NOTREACHED(); |
86 bool Channel::ChannelImpl::IsNamedServerInitialized( | |
87 const std::string& channel_id) { | |
88 return false; //file_util::PathExists(FilePath(channel_id)); | |
89 } | 304 } |
90 | 305 |
91 //------------------------------------------------------------------------------ | 306 //------------------------------------------------------------------------------ |
92 // Channel's methods simply call through to ChannelImpl. | 307 // Channel's methods simply call through to ChannelImpl. |
93 | 308 |
94 Channel::Channel(const IPC::ChannelHandle& channel_handle, | 309 Channel::Channel(const IPC::ChannelHandle& channel_handle, |
95 Mode mode, | 310 Mode mode, |
96 Listener* listener) | 311 Listener* listener) |
97 : channel_impl_(new ChannelImpl(channel_handle, mode, listener)) { | 312 : channel_impl_(new ChannelImpl(channel_handle, mode, listener)) { |
98 } | 313 } |
99 | 314 |
100 Channel::~Channel() { | 315 Channel::~Channel() { |
101 delete channel_impl_; | 316 delete channel_impl_; |
102 } | 317 } |
103 | 318 |
104 bool Channel::Connect() { | 319 bool Channel::Connect() { |
105 return channel_impl_->Connect(); | 320 return channel_impl_->Connect(); |
106 } | 321 } |
107 | 322 |
108 void Channel::Close() { | 323 void Channel::Close() { |
109 channel_impl_->Close(); | 324 channel_impl_->Close(); |
110 } | 325 } |
111 | 326 |
112 void Channel::set_listener(Listener* listener) { | 327 void Channel::set_listener(Listener* listener) { |
113 channel_impl_->set_listener(listener); | 328 channel_impl_->set_listener(listener); |
114 } | 329 } |
115 | 330 |
| 331 base::ProcessId Channel::peer_pid() const { |
| 332 // This shouldn't actually get used in the untrusted side of the proxy, and we |
| 333 // don't have the real pid anyway. |
| 334 return -1; |
| 335 } |
| 336 |
116 bool Channel::Send(Message* message) { | 337 bool Channel::Send(Message* message) { |
117 return channel_impl_->Send(message); | 338 return channel_impl_->Send(message); |
118 } | 339 } |
119 | 340 |
120 int Channel::GetClientFileDescriptor() const { | |
121 return channel_impl_->GetClientFileDescriptor(); | |
122 } | |
123 | |
124 int Channel::TakeClientFileDescriptor() { | |
125 return channel_impl_->TakeClientFileDescriptor(); | |
126 } | |
127 | |
128 bool Channel::AcceptsConnections() const { | |
129 return channel_impl_->AcceptsConnections(); | |
130 } | |
131 | |
132 bool Channel::HasAcceptedConnection() const { | |
133 return channel_impl_->HasAcceptedConnection(); | |
134 } | |
135 | |
136 bool Channel::GetClientEuid(uid_t* client_euid) const { | |
137 return channel_impl_->GetClientEuid(client_euid); | |
138 } | |
139 | |
140 void Channel::ResetToAcceptingConnectionState() { | |
141 channel_impl_->ResetToAcceptingConnectionState(); | |
142 } | |
143 | |
144 base::ProcessId Channel::peer_pid() const { return 0; } | |
145 | |
146 // static | 341 // static |
147 bool Channel::IsNamedServerInitialized(const std::string& channel_id) { | |
148 return ChannelImpl::IsNamedServerInitialized(channel_id); | |
149 } | |
150 | |
151 // static | |
152 std::string Channel::GenerateVerifiedChannelID(const std::string& prefix) { | 342 std::string Channel::GenerateVerifiedChannelID(const std::string& prefix) { |
153 // A random name is sufficient validation on posix systems, so we don't need | 343 // A random name is sufficient validation on posix systems, so we don't need |
154 // an additional shared secret. | 344 // an additional shared secret. |
155 std::string id = prefix; | 345 std::string id = prefix; |
156 if (!id.empty()) | 346 if (!id.empty()) |
157 id.append("."); | 347 id.append("."); |
158 | 348 |
159 return id.append(GenerateUniqueRandomChannelID()); | 349 return id.append(GenerateUniqueRandomChannelID()); |
160 } | 350 } |
161 | 351 |
162 } // namespace IPC | 352 } // namespace IPC |
OLD | NEW |