blob: 38e442a8320f018557d5ffaba1ef39146c5a53af [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"
initial.commit09911bf2008-07-26 23:55:2913#include "base/win_util.h"
[email protected]82e5ee82009-04-03 02:29:4514#include "chrome/common/chrome_counters.h"
15#include "chrome/common/ipc_logging.h"
16#include "chrome/common/ipc_message_utils.h"
initial.commit09911bf2008-07-26 23:55:2917
initial.commit09911bf2008-07-26 23:55:2918namespace IPC {
initial.commit09911bf2008-07-26 23:55:2919//------------------------------------------------------------------------------
20
[email protected]514411fc2008-12-10 22:28:1121Channel::ChannelImpl::State::State(ChannelImpl* channel) : is_pending(false) {
[email protected]17b89142008-11-07 21:52:1522 memset(&context.overlapped, 0, sizeof(context.overlapped));
23 context.handler = channel;
initial.commit09911bf2008-07-26 23:55:2924}
25
[email protected]514411fc2008-12-10 22:28:1126Channel::ChannelImpl::State::~State() {
27 COMPILE_ASSERT(!offsetof(Channel::ChannelImpl::State, context),
28 starts_with_io_context);
initial.commit09911bf2008-07-26 23:55:2929}
30
31//------------------------------------------------------------------------------
32
[email protected]514411fc2008-12-10 22:28:1133Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id, Mode mode,
34 Listener* listener)
[email protected]17b89142008-11-07 21:52:1535 : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
36 ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
37 pipe_(INVALID_HANDLE_VALUE),
initial.commit09911bf2008-07-26 23:55:2938 listener_(listener),
39 waiting_connect_(mode == MODE_SERVER),
[email protected]c1afbd2c2008-10-13 19:19:3640 processing_incoming_(false),
41 ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
initial.commit09911bf2008-07-26 23:55:2942 if (!CreatePipe(channel_id, mode)) {
43 // The pipe may have been closed already.
44 LOG(WARNING) << "Unable to create pipe named \"" << channel_id <<
45 "\" in " << (mode == 0 ? "server" : "client") << " mode.";
46 }
47}
48
[email protected]514411fc2008-12-10 22:28:1149void Channel::ChannelImpl::Close() {
[email protected]c1e4bff32009-01-29 00:07:0650 if (thread_check_.get()) {
51 DCHECK(thread_check_->CalledOnValidThread());
52 }
53
[email protected]17b89142008-11-07 21:52:1554 bool waited = false;
55 if (input_state_.is_pending || output_state_.is_pending) {
56 CancelIo(pipe_);
57 waited = true;
[email protected]ee78622d2008-10-13 21:25:5058 }
59
[email protected]17b89142008-11-07 21:52:1560 // Closing the handle at this point prevents us from issuing more requests
61 // form OnIOCompleted().
initial.commit09911bf2008-07-26 23:55:2962 if (pipe_ != INVALID_HANDLE_VALUE) {
63 CloseHandle(pipe_);
64 pipe_ = INVALID_HANDLE_VALUE;
65 }
66
[email protected]17b89142008-11-07 21:52:1567 // Make sure all IO has completed.
68 base::Time start = base::Time::Now();
69 while (input_state_.is_pending || output_state_.is_pending) {
70 MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this);
71 }
72 if (waited) {
73 // We want to see if we block the message loop for too long.
[email protected]553dba62009-02-24 19:08:2374 UMA_HISTOGRAM_TIMES("AsyncIO.IPCChannelClose", base::Time::Now() - start);
[email protected]17b89142008-11-07 21:52:1575 }
76
initial.commit09911bf2008-07-26 23:55:2977 while (!output_queue_.empty()) {
78 Message* m = output_queue_.front();
79 output_queue_.pop();
80 delete m;
81 }
82}
83
[email protected]514411fc2008-12-10 22:28:1184bool Channel::ChannelImpl::Send(Message* message) {
[email protected]c1e4bff32009-01-29 00:07:0685 DCHECK(thread_check_->CalledOnValidThread());
[email protected]82e5ee82009-04-03 02:29:4586 chrome::Counters::ipc_send_counter().Increment();
initial.commit09911bf2008-07-26 23:55:2987#ifdef IPC_MESSAGE_DEBUG_EXTRA
88 DLOG(INFO) << "sending message @" << message << " on channel @" << this
89 << " with type " << message->type()
90 << " (" << output_queue_.size() << " in queue)";
91#endif
92
93#ifdef IPC_MESSAGE_LOG_ENABLED
94 Logging::current()->OnSendMessage(message, L"");
95#endif
96
97 output_queue_.push(message);
98 // ensure waiting to write
99 if (!waiting_connect_) {
100 if (!output_state_.is_pending) {
[email protected]c1afbd2c2008-10-13 19:19:36101 if (!ProcessOutgoingMessages(NULL, 0))
initial.commit09911bf2008-07-26 23:55:29102 return false;
initial.commit09911bf2008-07-26 23:55:29103 }
104 }
105
106 return true;
107}
108
[email protected]d3216442009-03-05 21:07:27109const std::wstring Channel::ChannelImpl::PipeName(
110 const std::wstring& channel_id) const {
[email protected]514411fc2008-12-10 22:28:11111 std::wostringstream ss;
initial.commit09911bf2008-07-26 23:55:29112 // XXX(darin): get application name from somewhere else
113 ss << L"\\\\.\\pipe\\chrome." << channel_id;
114 return ss.str();
115}
116
[email protected]514411fc2008-12-10 22:28:11117bool Channel::ChannelImpl::CreatePipe(const std::wstring& channel_id,
118 Mode mode) {
initial.commit09911bf2008-07-26 23:55:29119 DCHECK(pipe_ == INVALID_HANDLE_VALUE);
[email protected]514411fc2008-12-10 22:28:11120 const std::wstring pipe_name = PipeName(channel_id);
initial.commit09911bf2008-07-26 23:55:29121 if (mode == MODE_SERVER) {
122 SECURITY_ATTRIBUTES security_attributes = {0};
123 security_attributes.bInheritHandle = FALSE;
124 security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
125 if (!win_util::GetLogonSessionOnlyDACL(
126 reinterpret_cast<SECURITY_DESCRIPTOR**>(
127 &security_attributes.lpSecurityDescriptor))) {
128 NOTREACHED();
129 }
130
131 pipe_ = CreateNamedPipeW(pipe_name.c_str(),
132 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
133 FILE_FLAG_FIRST_PIPE_INSTANCE,
134 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
135 1, // number of pipe instances
[email protected]514411fc2008-12-10 22:28:11136 // output buffer size (XXX tune)
137 Channel::kReadBufferSize,
138 // input buffer size (XXX tune)
139 Channel::kReadBufferSize,
initial.commit09911bf2008-07-26 23:55:29140 5000, // timeout in milliseconds (XXX tune)
141 &security_attributes);
142 LocalFree(security_attributes.lpSecurityDescriptor);
143 } else {
144 pipe_ = CreateFileW(pipe_name.c_str(),
145 GENERIC_READ | GENERIC_WRITE,
146 0,
147 NULL,
148 OPEN_EXISTING,
149 SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION |
150 FILE_FLAG_OVERLAPPED,
151 NULL);
152 }
153 if (pipe_ == INVALID_HANDLE_VALUE) {
154 // If this process is being closed, the pipe may be gone already.
155 LOG(WARNING) << "failed to create pipe: " << GetLastError();
156 return false;
157 }
158
159 // Create the Hello message to be sent when Connect is called
160 scoped_ptr<Message> m(new Message(MSG_ROUTING_NONE,
161 HELLO_MESSAGE_TYPE,
162 IPC::Message::PRIORITY_NORMAL));
163 if (!m->WriteInt(GetCurrentProcessId())) {
164 CloseHandle(pipe_);
165 pipe_ = INVALID_HANDLE_VALUE;
166 return false;
167 }
168
169 output_queue_.push(m.release());
170 return true;
171}
172
[email protected]514411fc2008-12-10 22:28:11173bool Channel::ChannelImpl::Connect() {
initial.commit09911bf2008-07-26 23:55:29174 DLOG(WARNING) << "Connect called twice";
175
[email protected]c1e4bff32009-01-29 00:07:06176 if (!thread_check_.get())
177 thread_check_.reset(new NonThreadSafe());
178
initial.commit09911bf2008-07-26 23:55:29179 if (pipe_ == INVALID_HANDLE_VALUE)
180 return false;
181
[email protected]c1afbd2c2008-10-13 19:19:36182 MessageLoopForIO::current()->RegisterIOHandler(pipe_, this);
183
initial.commit09911bf2008-07-26 23:55:29184 // Check to see if there is a client connected to our pipe...
185 if (waiting_connect_)
186 ProcessConnection();
187
188 if (!input_state_.is_pending) {
[email protected]c1afbd2c2008-10-13 19:19:36189 // Complete setup asynchronously. By not setting input_state_.is_pending
190 // to true, we indicate to OnIOCompleted that this is the special
191 // initialization signal.
192 MessageLoopForIO::current()->PostTask(FROM_HERE, factory_.NewRunnableMethod(
[email protected]514411fc2008-12-10 22:28:11193 &Channel::ChannelImpl::OnIOCompleted, &input_state_.context, 0, 0));
initial.commit09911bf2008-07-26 23:55:29194 }
195
196 if (!waiting_connect_)
[email protected]c1afbd2c2008-10-13 19:19:36197 ProcessOutgoingMessages(NULL, 0);
initial.commit09911bf2008-07-26 23:55:29198 return true;
199}
200
[email protected]514411fc2008-12-10 22:28:11201bool Channel::ChannelImpl::ProcessConnection() {
[email protected]c1e4bff32009-01-29 00:07:06202 DCHECK(thread_check_->CalledOnValidThread());
[email protected]17b89142008-11-07 21:52:15203 if (input_state_.is_pending)
[email protected]c1afbd2c2008-10-13 19:19:36204 input_state_.is_pending = false;
initial.commit09911bf2008-07-26 23:55:29205
206 // Do we have a client connected to our pipe?
[email protected]17b89142008-11-07 21:52:15207 if (INVALID_HANDLE_VALUE == pipe_)
208 return false;
209
210 BOOL ok = ConnectNamedPipe(pipe_, &input_state_.context.overlapped);
initial.commit09911bf2008-07-26 23:55:29211
212 DWORD err = GetLastError();
213 if (ok) {
214 // Uhm, the API documentation says that this function should never
215 // return success when used in overlapped mode.
216 NOTREACHED();
217 return false;
218 }
219
220 switch (err) {
221 case ERROR_IO_PENDING:
222 input_state_.is_pending = true;
initial.commit09911bf2008-07-26 23:55:29223 break;
224 case ERROR_PIPE_CONNECTED:
225 waiting_connect_ = false;
226 break;
227 default:
228 NOTREACHED();
229 return false;
230 }
231
232 return true;
233}
234
[email protected]514411fc2008-12-10 22:28:11235bool Channel::ChannelImpl::ProcessIncomingMessages(
236 MessageLoopForIO::IOContext* context,
237 DWORD bytes_read) {
[email protected]c1e4bff32009-01-29 00:07:06238 DCHECK(thread_check_->CalledOnValidThread());
initial.commit09911bf2008-07-26 23:55:29239 if (input_state_.is_pending) {
240 input_state_.is_pending = false;
[email protected]c1afbd2c2008-10-13 19:19:36241 DCHECK(context);
initial.commit09911bf2008-07-26 23:55:29242
[email protected]c1afbd2c2008-10-13 19:19:36243 if (!context || !bytes_read)
initial.commit09911bf2008-07-26 23:55:29244 return false;
initial.commit09911bf2008-07-26 23:55:29245 } else {
[email protected]c1afbd2c2008-10-13 19:19:36246 // This happens at channel initialization.
[email protected]17b89142008-11-07 21:52:15247 DCHECK(!bytes_read && context == &input_state_.context);
initial.commit09911bf2008-07-26 23:55:29248 }
249
250 for (;;) {
251 if (bytes_read == 0) {
[email protected]17b89142008-11-07 21:52:15252 if (INVALID_HANDLE_VALUE == pipe_)
253 return false;
254
[email protected]c1afbd2c2008-10-13 19:19:36255 // Read from pipe...
initial.commit09911bf2008-07-26 23:55:29256 BOOL ok = ReadFile(pipe_,
257 input_buf_,
[email protected]514411fc2008-12-10 22:28:11258 Channel::kReadBufferSize,
initial.commit09911bf2008-07-26 23:55:29259 &bytes_read,
[email protected]17b89142008-11-07 21:52:15260 &input_state_.context.overlapped);
initial.commit09911bf2008-07-26 23:55:29261 if (!ok) {
262 DWORD err = GetLastError();
263 if (err == ERROR_IO_PENDING) {
initial.commit09911bf2008-07-26 23:55:29264 input_state_.is_pending = true;
265 return true;
266 }
267 LOG(ERROR) << "pipe error: " << err;
268 return false;
269 }
[email protected]17b89142008-11-07 21:52:15270 input_state_.is_pending = true;
271 return true;
initial.commit09911bf2008-07-26 23:55:29272 }
273 DCHECK(bytes_read);
274
[email protected]c1afbd2c2008-10-13 19:19:36275 // Process messages from input buffer.
initial.commit09911bf2008-07-26 23:55:29276
277 const char* p, *end;
278 if (input_overflow_buf_.empty()) {
279 p = input_buf_;
280 end = p + bytes_read;
281 } else {
282 if (input_overflow_buf_.size() > (kMaximumMessageSize - bytes_read)) {
283 input_overflow_buf_.clear();
284 LOG(ERROR) << "IPC message is too big";
285 return false;
286 }
287 input_overflow_buf_.append(input_buf_, bytes_read);
288 p = input_overflow_buf_.data();
289 end = p + input_overflow_buf_.size();
290 }
291
292 while (p < end) {
293 const char* message_tail = Message::FindNext(p, end);
294 if (message_tail) {
295 int len = static_cast<int>(message_tail - p);
296 const Message m(p, len);
297#ifdef IPC_MESSAGE_DEBUG_EXTRA
[email protected]c1afbd2c2008-10-13 19:19:36298 DLOG(INFO) << "received message on channel @" << this <<
299 " with type " << m.type();
initial.commit09911bf2008-07-26 23:55:29300#endif
301 if (m.routing_id() == MSG_ROUTING_NONE &&
302 m.type() == HELLO_MESSAGE_TYPE) {
303 // The Hello message contains only the process id.
304 listener_->OnChannelConnected(MessageIterator(m).NextInt());
305 } else {
306 listener_->OnMessageReceived(m);
307 }
308 p = message_tail;
309 } else {
[email protected]c1afbd2c2008-10-13 19:19:36310 // Last message is partial.
initial.commit09911bf2008-07-26 23:55:29311 break;
312 }
313 }
314 input_overflow_buf_.assign(p, end - p);
315
[email protected]c1afbd2c2008-10-13 19:19:36316 bytes_read = 0; // Get more data.
initial.commit09911bf2008-07-26 23:55:29317 }
318
319 return true;
320}
321
[email protected]514411fc2008-12-10 22:28:11322bool Channel::ChannelImpl::ProcessOutgoingMessages(
323 MessageLoopForIO::IOContext* context,
324 DWORD bytes_written) {
initial.commit09911bf2008-07-26 23:55:29325 DCHECK(!waiting_connect_); // Why are we trying to send messages if there's
326 // no connection?
[email protected]c1e4bff32009-01-29 00:07:06327 DCHECK(thread_check_->CalledOnValidThread());
initial.commit09911bf2008-07-26 23:55:29328
329 if (output_state_.is_pending) {
[email protected]c1afbd2c2008-10-13 19:19:36330 DCHECK(context);
initial.commit09911bf2008-07-26 23:55:29331 output_state_.is_pending = false;
[email protected]c1afbd2c2008-10-13 19:19:36332 if (!context || bytes_written == 0) {
initial.commit09911bf2008-07-26 23:55:29333 DWORD err = GetLastError();
334 LOG(ERROR) << "pipe error: " << err;
335 return false;
336 }
[email protected]c1afbd2c2008-10-13 19:19:36337 // Message was sent.
initial.commit09911bf2008-07-26 23:55:29338 DCHECK(!output_queue_.empty());
339 Message* m = output_queue_.front();
340 output_queue_.pop();
341 delete m;
342 }
343
[email protected]17b89142008-11-07 21:52:15344 if (output_queue_.empty())
345 return true;
346
347 if (INVALID_HANDLE_VALUE == pipe_)
348 return false;
349
350 // Write to pipe...
351 Message* m = output_queue_.front();
352 BOOL ok = WriteFile(pipe_,
353 m->data(),
354 m->size(),
355 &bytes_written,
356 &output_state_.context.overlapped);
357 if (!ok) {
358 DWORD err = GetLastError();
359 if (err == ERROR_IO_PENDING) {
360 output_state_.is_pending = true;
initial.commit09911bf2008-07-26 23:55:29361
362#ifdef IPC_MESSAGE_DEBUG_EXTRA
[email protected]17b89142008-11-07 21:52:15363 DLOG(INFO) << "sent pending message @" << m << " on channel @" <<
364 this << " with type " << m->type();
initial.commit09911bf2008-07-26 23:55:29365#endif
366
[email protected]17b89142008-11-07 21:52:15367 return true;
initial.commit09911bf2008-07-26 23:55:29368 }
[email protected]17b89142008-11-07 21:52:15369 LOG(ERROR) << "pipe error: " << err;
370 return false;
initial.commit09911bf2008-07-26 23:55:29371 }
372
[email protected]17b89142008-11-07 21:52:15373#ifdef IPC_MESSAGE_DEBUG_EXTRA
374 DLOG(INFO) << "sent message @" << m << " on channel @" << this <<
375 " with type " << m->type();
376#endif
377
378 output_state_.is_pending = true;
initial.commit09911bf2008-07-26 23:55:29379 return true;
380}
381
[email protected]514411fc2008-12-10 22:28:11382void Channel::ChannelImpl::OnIOCompleted(MessageLoopForIO::IOContext* context,
[email protected]17b89142008-11-07 21:52:15383 DWORD bytes_transfered, DWORD error) {
initial.commit09911bf2008-07-26 23:55:29384 bool ok;
[email protected]c1e4bff32009-01-29 00:07:06385 DCHECK(thread_check_->CalledOnValidThread());
[email protected]17b89142008-11-07 21:52:15386 if (context == &input_state_.context) {
initial.commit09911bf2008-07-26 23:55:29387 if (waiting_connect_) {
[email protected]17b89142008-11-07 21:52:15388 if (!ProcessConnection())
389 return;
initial.commit09911bf2008-07-26 23:55:29390 // We may have some messages queued up to send...
391 if (!output_queue_.empty() && !output_state_.is_pending)
[email protected]c1afbd2c2008-10-13 19:19:36392 ProcessOutgoingMessages(NULL, 0);
initial.commit09911bf2008-07-26 23:55:29393 if (input_state_.is_pending)
394 return;
395 // else, fall-through and look for incoming messages...
396 }
397 // we don't support recursion through OnMessageReceived yet!
398 DCHECK(!processing_incoming_);
399 processing_incoming_ = true;
[email protected]c1afbd2c2008-10-13 19:19:36400 ok = ProcessIncomingMessages(context, bytes_transfered);
initial.commit09911bf2008-07-26 23:55:29401 processing_incoming_ = false;
402 } else {
[email protected]17b89142008-11-07 21:52:15403 DCHECK(context == &output_state_.context);
[email protected]c1afbd2c2008-10-13 19:19:36404 ok = ProcessOutgoingMessages(context, bytes_transfered);
initial.commit09911bf2008-07-26 23:55:29405 }
[email protected]17b89142008-11-07 21:52:15406 if (!ok && INVALID_HANDLE_VALUE != pipe_) {
407 // We don't want to re-enter Close().
initial.commit09911bf2008-07-26 23:55:29408 Close();
409 listener_->OnChannelError();
410 }
411}
412
[email protected]514411fc2008-12-10 22:28:11413//------------------------------------------------------------------------------
414// Channel's methods simply call through to ChannelImpl.
415Channel::Channel(const std::wstring& channel_id, Mode mode,
416 Listener* listener)
417 : channel_impl_(new ChannelImpl(channel_id, mode, listener)) {
initial.commit09911bf2008-07-26 23:55:29418}
[email protected]514411fc2008-12-10 22:28:11419
420Channel::~Channel() {
421 delete channel_impl_;
422}
423
424bool Channel::Connect() {
425 return channel_impl_->Connect();
426}
427
428void Channel::Close() {
429 channel_impl_->Close();
430}
431
432void Channel::set_listener(Listener* listener) {
433 channel_impl_->set_listener(listener);
434}
435
436bool Channel::Send(Message* message) {
437 return channel_impl_->Send(message);
438}
439
440} // namespace IPC