blob: d4e7c3188e62020b743a541ecd289b4497aa0c36 [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294
[email protected]82e5ee82009-04-03 02:29:455#include "chrome/common/ipc_channel_win.h"
[email protected]c1afbd2c2008-10-13 19:19:366
initial.commit09911bf2008-07-26 23:55:297#include <windows.h>
8#include <sstream>
9
[email protected]c1afbd2c2008-10-13 19:19:3610#include "base/compiler_specific.h"
initial.commit09911bf2008-07-26 23:55:2911#include "base/logging.h"
[email protected]c1e4bff32009-01-29 00:07:0612#include "base/non_thread_safe.h"
[email protected]e6f02ab2009-04-10 22:29:2913#include "base/stats_counters.h"
initial.commit09911bf2008-07-26 23:55:2914#include "base/win_util.h"
[email protected]82e5ee82009-04-03 02:29:4515#include "chrome/common/chrome_counters.h"
16#include "chrome/common/ipc_logging.h"
17#include "chrome/common/ipc_message_utils.h"
initial.commit09911bf2008-07-26 23:55:2918
initial.commit09911bf2008-07-26 23:55:2919namespace IPC {
initial.commit09911bf2008-07-26 23:55:2920//------------------------------------------------------------------------------
21
[email protected]514411fc2008-12-10 22:28:1122Channel::ChannelImpl::State::State(ChannelImpl* channel) : is_pending(false) {
[email protected]17b89142008-11-07 21:52:1523 memset(&context.overlapped, 0, sizeof(context.overlapped));
24 context.handler = channel;
initial.commit09911bf2008-07-26 23:55:2925}
26
[email protected]514411fc2008-12-10 22:28:1127Channel::ChannelImpl::State::~State() {
28 COMPILE_ASSERT(!offsetof(Channel::ChannelImpl::State, context),
29 starts_with_io_context);
initial.commit09911bf2008-07-26 23:55:2930}
31
32//------------------------------------------------------------------------------
33
[email protected]514411fc2008-12-10 22:28:1134Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id, Mode mode,
35 Listener* listener)
[email protected]17b89142008-11-07 21:52:1536 : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
37 ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
38 pipe_(INVALID_HANDLE_VALUE),
initial.commit09911bf2008-07-26 23:55:2939 listener_(listener),
40 waiting_connect_(mode == MODE_SERVER),
[email protected]c1afbd2c2008-10-13 19:19:3641 processing_incoming_(false),
42 ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
initial.commit09911bf2008-07-26 23:55:2943 if (!CreatePipe(channel_id, mode)) {
44 // The pipe may have been closed already.
45 LOG(WARNING) << "Unable to create pipe named \"" << channel_id <<
46 "\" in " << (mode == 0 ? "server" : "client") << " mode.";
47 }
48}
49
[email protected]514411fc2008-12-10 22:28:1150void Channel::ChannelImpl::Close() {
[email protected]c1e4bff32009-01-29 00:07:0651 if (thread_check_.get()) {
52 DCHECK(thread_check_->CalledOnValidThread());
53 }
54
[email protected]17b89142008-11-07 21:52:1555 bool waited = false;
56 if (input_state_.is_pending || output_state_.is_pending) {
57 CancelIo(pipe_);
58 waited = true;
[email protected]ee78622d2008-10-13 21:25:5059 }
60
[email protected]17b89142008-11-07 21:52:1561 // Closing the handle at this point prevents us from issuing more requests
62 // form OnIOCompleted().
initial.commit09911bf2008-07-26 23:55:2963 if (pipe_ != INVALID_HANDLE_VALUE) {
64 CloseHandle(pipe_);
65 pipe_ = INVALID_HANDLE_VALUE;
66 }
67
[email protected]17b89142008-11-07 21:52:1568 // Make sure all IO has completed.
69 base::Time start = base::Time::Now();
70 while (input_state_.is_pending || output_state_.is_pending) {
71 MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this);
72 }
73 if (waited) {
74 // We want to see if we block the message loop for too long.
[email protected]553dba62009-02-24 19:08:2375 UMA_HISTOGRAM_TIMES("AsyncIO.IPCChannelClose", base::Time::Now() - start);
[email protected]17b89142008-11-07 21:52:1576 }
77
initial.commit09911bf2008-07-26 23:55:2978 while (!output_queue_.empty()) {
79 Message* m = output_queue_.front();
80 output_queue_.pop();
81 delete m;
82 }
83}
84
[email protected]514411fc2008-12-10 22:28:1185bool Channel::ChannelImpl::Send(Message* message) {
[email protected]c1e4bff32009-01-29 00:07:0686 DCHECK(thread_check_->CalledOnValidThread());
[email protected]82e5ee82009-04-03 02:29:4587 chrome::Counters::ipc_send_counter().Increment();
initial.commit09911bf2008-07-26 23:55:2988#ifdef IPC_MESSAGE_DEBUG_EXTRA
89 DLOG(INFO) << "sending message @" << message << " on channel @" << this
90 << " with type " << message->type()
91 << " (" << output_queue_.size() << " in queue)";
92#endif
93
94#ifdef IPC_MESSAGE_LOG_ENABLED
95 Logging::current()->OnSendMessage(message, L"");
96#endif
97
98 output_queue_.push(message);
99 // ensure waiting to write
100 if (!waiting_connect_) {
101 if (!output_state_.is_pending) {
[email protected]c1afbd2c2008-10-13 19:19:36102 if (!ProcessOutgoingMessages(NULL, 0))
initial.commit09911bf2008-07-26 23:55:29103 return false;
initial.commit09911bf2008-07-26 23:55:29104 }
105 }
106
107 return true;
108}
109
[email protected]d3216442009-03-05 21:07:27110const std::wstring Channel::ChannelImpl::PipeName(
111 const std::wstring& channel_id) const {
[email protected]514411fc2008-12-10 22:28:11112 std::wostringstream ss;
initial.commit09911bf2008-07-26 23:55:29113 // XXX(darin): get application name from somewhere else
114 ss << L"\\\\.\\pipe\\chrome." << channel_id;
115 return ss.str();
116}
117
[email protected]514411fc2008-12-10 22:28:11118bool Channel::ChannelImpl::CreatePipe(const std::wstring& channel_id,
119 Mode mode) {
initial.commit09911bf2008-07-26 23:55:29120 DCHECK(pipe_ == INVALID_HANDLE_VALUE);
[email protected]514411fc2008-12-10 22:28:11121 const std::wstring pipe_name = PipeName(channel_id);
initial.commit09911bf2008-07-26 23:55:29122 if (mode == MODE_SERVER) {
123 SECURITY_ATTRIBUTES security_attributes = {0};
124 security_attributes.bInheritHandle = FALSE;
125 security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
126 if (!win_util::GetLogonSessionOnlyDACL(
127 reinterpret_cast<SECURITY_DESCRIPTOR**>(
128 &security_attributes.lpSecurityDescriptor))) {
129 NOTREACHED();
130 }
131
132 pipe_ = CreateNamedPipeW(pipe_name.c_str(),
133 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
134 FILE_FLAG_FIRST_PIPE_INSTANCE,
135 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
136 1, // number of pipe instances
[email protected]514411fc2008-12-10 22:28:11137 // output buffer size (XXX tune)
138 Channel::kReadBufferSize,
139 // input buffer size (XXX tune)
140 Channel::kReadBufferSize,
initial.commit09911bf2008-07-26 23:55:29141 5000, // timeout in milliseconds (XXX tune)
142 &security_attributes);
143 LocalFree(security_attributes.lpSecurityDescriptor);
144 } else {
145 pipe_ = CreateFileW(pipe_name.c_str(),
146 GENERIC_READ | GENERIC_WRITE,
147 0,
148 NULL,
149 OPEN_EXISTING,
150 SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION |
151 FILE_FLAG_OVERLAPPED,
152 NULL);
153 }
154 if (pipe_ == INVALID_HANDLE_VALUE) {
155 // If this process is being closed, the pipe may be gone already.
156 LOG(WARNING) << "failed to create pipe: " << GetLastError();
157 return false;
158 }
159
160 // Create the Hello message to be sent when Connect is called
161 scoped_ptr<Message> m(new Message(MSG_ROUTING_NONE,
162 HELLO_MESSAGE_TYPE,
163 IPC::Message::PRIORITY_NORMAL));
164 if (!m->WriteInt(GetCurrentProcessId())) {
165 CloseHandle(pipe_);
166 pipe_ = INVALID_HANDLE_VALUE;
167 return false;
168 }
169
170 output_queue_.push(m.release());
171 return true;
172}
173
[email protected]514411fc2008-12-10 22:28:11174bool Channel::ChannelImpl::Connect() {
initial.commit09911bf2008-07-26 23:55:29175 DLOG(WARNING) << "Connect called twice";
176
[email protected]c1e4bff32009-01-29 00:07:06177 if (!thread_check_.get())
178 thread_check_.reset(new NonThreadSafe());
179
initial.commit09911bf2008-07-26 23:55:29180 if (pipe_ == INVALID_HANDLE_VALUE)
181 return false;
182
[email protected]c1afbd2c2008-10-13 19:19:36183 MessageLoopForIO::current()->RegisterIOHandler(pipe_, this);
184
initial.commit09911bf2008-07-26 23:55:29185 // Check to see if there is a client connected to our pipe...
186 if (waiting_connect_)
187 ProcessConnection();
188
189 if (!input_state_.is_pending) {
[email protected]c1afbd2c2008-10-13 19:19:36190 // Complete setup asynchronously. By not setting input_state_.is_pending
191 // to true, we indicate to OnIOCompleted that this is the special
192 // initialization signal.
193 MessageLoopForIO::current()->PostTask(FROM_HERE, factory_.NewRunnableMethod(
[email protected]514411fc2008-12-10 22:28:11194 &Channel::ChannelImpl::OnIOCompleted, &input_state_.context, 0, 0));
initial.commit09911bf2008-07-26 23:55:29195 }
196
197 if (!waiting_connect_)
[email protected]c1afbd2c2008-10-13 19:19:36198 ProcessOutgoingMessages(NULL, 0);
initial.commit09911bf2008-07-26 23:55:29199 return true;
200}
201
[email protected]514411fc2008-12-10 22:28:11202bool Channel::ChannelImpl::ProcessConnection() {
[email protected]c1e4bff32009-01-29 00:07:06203 DCHECK(thread_check_->CalledOnValidThread());
[email protected]17b89142008-11-07 21:52:15204 if (input_state_.is_pending)
[email protected]c1afbd2c2008-10-13 19:19:36205 input_state_.is_pending = false;
initial.commit09911bf2008-07-26 23:55:29206
207 // Do we have a client connected to our pipe?
[email protected]17b89142008-11-07 21:52:15208 if (INVALID_HANDLE_VALUE == pipe_)
209 return false;
210
211 BOOL ok = ConnectNamedPipe(pipe_, &input_state_.context.overlapped);
initial.commit09911bf2008-07-26 23:55:29212
213 DWORD err = GetLastError();
214 if (ok) {
215 // Uhm, the API documentation says that this function should never
216 // return success when used in overlapped mode.
217 NOTREACHED();
218 return false;
219 }
220
221 switch (err) {
222 case ERROR_IO_PENDING:
223 input_state_.is_pending = true;
initial.commit09911bf2008-07-26 23:55:29224 break;
225 case ERROR_PIPE_CONNECTED:
226 waiting_connect_ = false;
227 break;
228 default:
229 NOTREACHED();
230 return false;
231 }
232
233 return true;
234}
235
[email protected]514411fc2008-12-10 22:28:11236bool Channel::ChannelImpl::ProcessIncomingMessages(
237 MessageLoopForIO::IOContext* context,
238 DWORD bytes_read) {
[email protected]c1e4bff32009-01-29 00:07:06239 DCHECK(thread_check_->CalledOnValidThread());
initial.commit09911bf2008-07-26 23:55:29240 if (input_state_.is_pending) {
241 input_state_.is_pending = false;
[email protected]c1afbd2c2008-10-13 19:19:36242 DCHECK(context);
initial.commit09911bf2008-07-26 23:55:29243
[email protected]c1afbd2c2008-10-13 19:19:36244 if (!context || !bytes_read)
initial.commit09911bf2008-07-26 23:55:29245 return false;
initial.commit09911bf2008-07-26 23:55:29246 } else {
[email protected]c1afbd2c2008-10-13 19:19:36247 // This happens at channel initialization.
[email protected]17b89142008-11-07 21:52:15248 DCHECK(!bytes_read && context == &input_state_.context);
initial.commit09911bf2008-07-26 23:55:29249 }
250
251 for (;;) {
252 if (bytes_read == 0) {
[email protected]17b89142008-11-07 21:52:15253 if (INVALID_HANDLE_VALUE == pipe_)
254 return false;
255
[email protected]c1afbd2c2008-10-13 19:19:36256 // Read from pipe...
initial.commit09911bf2008-07-26 23:55:29257 BOOL ok = ReadFile(pipe_,
258 input_buf_,
[email protected]514411fc2008-12-10 22:28:11259 Channel::kReadBufferSize,
initial.commit09911bf2008-07-26 23:55:29260 &bytes_read,
[email protected]17b89142008-11-07 21:52:15261 &input_state_.context.overlapped);
initial.commit09911bf2008-07-26 23:55:29262 if (!ok) {
263 DWORD err = GetLastError();
264 if (err == ERROR_IO_PENDING) {
initial.commit09911bf2008-07-26 23:55:29265 input_state_.is_pending = true;
266 return true;
267 }
268 LOG(ERROR) << "pipe error: " << err;
269 return false;
270 }
[email protected]17b89142008-11-07 21:52:15271 input_state_.is_pending = true;
272 return true;
initial.commit09911bf2008-07-26 23:55:29273 }
274 DCHECK(bytes_read);
275
[email protected]c1afbd2c2008-10-13 19:19:36276 // Process messages from input buffer.
initial.commit09911bf2008-07-26 23:55:29277
278 const char* p, *end;
279 if (input_overflow_buf_.empty()) {
280 p = input_buf_;
281 end = p + bytes_read;
282 } else {
283 if (input_overflow_buf_.size() > (kMaximumMessageSize - bytes_read)) {
284 input_overflow_buf_.clear();
285 LOG(ERROR) << "IPC message is too big";
286 return false;
287 }
288 input_overflow_buf_.append(input_buf_, bytes_read);
289 p = input_overflow_buf_.data();
290 end = p + input_overflow_buf_.size();
291 }
292
293 while (p < end) {
294 const char* message_tail = Message::FindNext(p, end);
295 if (message_tail) {
296 int len = static_cast<int>(message_tail - p);
297 const Message m(p, len);
298#ifdef IPC_MESSAGE_DEBUG_EXTRA
[email protected]c1afbd2c2008-10-13 19:19:36299 DLOG(INFO) << "received message on channel @" << this <<
300 " with type " << m.type();
initial.commit09911bf2008-07-26 23:55:29301#endif
302 if (m.routing_id() == MSG_ROUTING_NONE &&
303 m.type() == HELLO_MESSAGE_TYPE) {
304 // The Hello message contains only the process id.
305 listener_->OnChannelConnected(MessageIterator(m).NextInt());
306 } else {
307 listener_->OnMessageReceived(m);
308 }
309 p = message_tail;
310 } else {
[email protected]c1afbd2c2008-10-13 19:19:36311 // Last message is partial.
initial.commit09911bf2008-07-26 23:55:29312 break;
313 }
314 }
315 input_overflow_buf_.assign(p, end - p);
316
[email protected]c1afbd2c2008-10-13 19:19:36317 bytes_read = 0; // Get more data.
initial.commit09911bf2008-07-26 23:55:29318 }
319
320 return true;
321}
322
[email protected]514411fc2008-12-10 22:28:11323bool Channel::ChannelImpl::ProcessOutgoingMessages(
324 MessageLoopForIO::IOContext* context,
325 DWORD bytes_written) {
initial.commit09911bf2008-07-26 23:55:29326 DCHECK(!waiting_connect_); // Why are we trying to send messages if there's
327 // no connection?
[email protected]c1e4bff32009-01-29 00:07:06328 DCHECK(thread_check_->CalledOnValidThread());
initial.commit09911bf2008-07-26 23:55:29329
330 if (output_state_.is_pending) {
[email protected]c1afbd2c2008-10-13 19:19:36331 DCHECK(context);
initial.commit09911bf2008-07-26 23:55:29332 output_state_.is_pending = false;
[email protected]c1afbd2c2008-10-13 19:19:36333 if (!context || bytes_written == 0) {
initial.commit09911bf2008-07-26 23:55:29334 DWORD err = GetLastError();
335 LOG(ERROR) << "pipe error: " << err;
336 return false;
337 }
[email protected]c1afbd2c2008-10-13 19:19:36338 // Message was sent.
initial.commit09911bf2008-07-26 23:55:29339 DCHECK(!output_queue_.empty());
340 Message* m = output_queue_.front();
341 output_queue_.pop();
342 delete m;
343 }
344
[email protected]17b89142008-11-07 21:52:15345 if (output_queue_.empty())
346 return true;
347
348 if (INVALID_HANDLE_VALUE == pipe_)
349 return false;
350
351 // Write to pipe...
352 Message* m = output_queue_.front();
353 BOOL ok = WriteFile(pipe_,
354 m->data(),
355 m->size(),
356 &bytes_written,
357 &output_state_.context.overlapped);
358 if (!ok) {
359 DWORD err = GetLastError();
360 if (err == ERROR_IO_PENDING) {
361 output_state_.is_pending = true;
initial.commit09911bf2008-07-26 23:55:29362
363#ifdef IPC_MESSAGE_DEBUG_EXTRA
[email protected]17b89142008-11-07 21:52:15364 DLOG(INFO) << "sent pending message @" << m << " on channel @" <<
365 this << " with type " << m->type();
initial.commit09911bf2008-07-26 23:55:29366#endif
367
[email protected]17b89142008-11-07 21:52:15368 return true;
initial.commit09911bf2008-07-26 23:55:29369 }
[email protected]17b89142008-11-07 21:52:15370 LOG(ERROR) << "pipe error: " << err;
371 return false;
initial.commit09911bf2008-07-26 23:55:29372 }
373
[email protected]17b89142008-11-07 21:52:15374#ifdef IPC_MESSAGE_DEBUG_EXTRA
375 DLOG(INFO) << "sent message @" << m << " on channel @" << this <<
376 " with type " << m->type();
377#endif
378
379 output_state_.is_pending = true;
initial.commit09911bf2008-07-26 23:55:29380 return true;
381}
382
[email protected]514411fc2008-12-10 22:28:11383void Channel::ChannelImpl::OnIOCompleted(MessageLoopForIO::IOContext* context,
[email protected]17b89142008-11-07 21:52:15384 DWORD bytes_transfered, DWORD error) {
initial.commit09911bf2008-07-26 23:55:29385 bool ok;
[email protected]c1e4bff32009-01-29 00:07:06386 DCHECK(thread_check_->CalledOnValidThread());
[email protected]17b89142008-11-07 21:52:15387 if (context == &input_state_.context) {
initial.commit09911bf2008-07-26 23:55:29388 if (waiting_connect_) {
[email protected]17b89142008-11-07 21:52:15389 if (!ProcessConnection())
390 return;
initial.commit09911bf2008-07-26 23:55:29391 // We may have some messages queued up to send...
392 if (!output_queue_.empty() && !output_state_.is_pending)
[email protected]c1afbd2c2008-10-13 19:19:36393 ProcessOutgoingMessages(NULL, 0);
initial.commit09911bf2008-07-26 23:55:29394 if (input_state_.is_pending)
395 return;
396 // else, fall-through and look for incoming messages...
397 }
398 // we don't support recursion through OnMessageReceived yet!
399 DCHECK(!processing_incoming_);
400 processing_incoming_ = true;
[email protected]c1afbd2c2008-10-13 19:19:36401 ok = ProcessIncomingMessages(context, bytes_transfered);
initial.commit09911bf2008-07-26 23:55:29402 processing_incoming_ = false;
403 } else {
[email protected]17b89142008-11-07 21:52:15404 DCHECK(context == &output_state_.context);
[email protected]c1afbd2c2008-10-13 19:19:36405 ok = ProcessOutgoingMessages(context, bytes_transfered);
initial.commit09911bf2008-07-26 23:55:29406 }
[email protected]17b89142008-11-07 21:52:15407 if (!ok && INVALID_HANDLE_VALUE != pipe_) {
408 // We don't want to re-enter Close().
initial.commit09911bf2008-07-26 23:55:29409 Close();
410 listener_->OnChannelError();
411 }
412}
413
[email protected]514411fc2008-12-10 22:28:11414//------------------------------------------------------------------------------
415// Channel's methods simply call through to ChannelImpl.
416Channel::Channel(const std::wstring& channel_id, Mode mode,
417 Listener* listener)
418 : channel_impl_(new ChannelImpl(channel_id, mode, listener)) {
initial.commit09911bf2008-07-26 23:55:29419}
[email protected]514411fc2008-12-10 22:28:11420
421Channel::~Channel() {
422 delete channel_impl_;
423}
424
425bool Channel::Connect() {
426 return channel_impl_->Connect();
427}
428
429void Channel::Close() {
430 channel_impl_->Close();
431}
432
433void Channel::set_listener(Listener* listener) {
434 channel_impl_->set_listener(listener);
435}
436
437bool Channel::Send(Message* message) {
438 return channel_impl_->Send(message);
439}
440
441} // namespace IPC