blob: 40a101bd9f8dbbd10704e29c42b1b1052b5b9571 [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
5#include <windows.h>
6#include <sstream>
7
8#include "chrome/common/chrome_counters.h"
9#include "chrome/common/ipc_channel.h"
10
11#include "base/logging.h"
12#include "base/win_util.h"
13#include "chrome/common/ipc_logging.h"
14#include "chrome/common/ipc_message_utils.h"
15
16using namespace std;
17
18namespace IPC {
19
20//------------------------------------------------------------------------------
21
22Channel::State::State()
23 : is_pending(false) {
24 memset(&overlapped, 0, sizeof(overlapped));
25 overlapped.hEvent = CreateEvent(NULL, // default security attributes
26 TRUE, // manual-reset event
27 TRUE, // initial state = signaled
28 NULL); // unnamed event object
29}
30
31Channel::State::~State() {
32 if (overlapped.hEvent)
33 CloseHandle(overlapped.hEvent);
34}
35
36//------------------------------------------------------------------------------
37
38Channel::Channel(const wstring& channel_id, Mode mode, Listener* listener)
39 : pipe_(INVALID_HANDLE_VALUE),
40 listener_(listener),
41 waiting_connect_(mode == MODE_SERVER),
42 processing_incoming_(false) {
43 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
50void Channel::Close() {
51 // make sure we are no longer watching the pipe events
[email protected]2de75522008-08-09 08:34:0852 MessageLoop::current()->WatchObject(input_state_.overlapped.hEvent, NULL);
53 MessageLoop::current()->WatchObject(output_state_.overlapped.hEvent, NULL);
initial.commit09911bf2008-07-26 23:55:2954
55 if (pipe_ != INVALID_HANDLE_VALUE) {
56 CloseHandle(pipe_);
57 pipe_ = INVALID_HANDLE_VALUE;
58 }
59
60 while (!output_queue_.empty()) {
61 Message* m = output_queue_.front();
62 output_queue_.pop();
63 delete m;
64 }
65}
66
67bool Channel::Send(Message* message) {
68 chrome::Counters::ipc_send_counter().Increment();
69#ifdef IPC_MESSAGE_DEBUG_EXTRA
70 DLOG(INFO) << "sending message @" << message << " on channel @" << this
71 << " with type " << message->type()
72 << " (" << output_queue_.size() << " in queue)";
73#endif
74
75#ifdef IPC_MESSAGE_LOG_ENABLED
76 Logging::current()->OnSendMessage(message, L"");
77#endif
78
79 output_queue_.push(message);
80 // ensure waiting to write
81 if (!waiting_connect_) {
82 if (!output_state_.is_pending) {
83 if (!ProcessOutgoingMessages())
84 return false;
85 } else if (WaitForSingleObject(output_state_.overlapped.hEvent, 0) ==
86 WAIT_OBJECT_0) {
87 OnObjectSignaled(output_state_.overlapped.hEvent);
88 }
89 }
90
91 return true;
92}
93
94const wstring Channel::PipeName(const wstring& channel_id) const {
95 wostringstream ss;
96 // XXX(darin): get application name from somewhere else
97 ss << L"\\\\.\\pipe\\chrome." << channel_id;
98 return ss.str();
99}
100
101bool Channel::CreatePipe(const wstring& channel_id, Mode mode) {
102 DCHECK(pipe_ == INVALID_HANDLE_VALUE);
103 const wstring pipe_name = PipeName(channel_id);
104 if (mode == MODE_SERVER) {
105 SECURITY_ATTRIBUTES security_attributes = {0};
106 security_attributes.bInheritHandle = FALSE;
107 security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
108 if (!win_util::GetLogonSessionOnlyDACL(
109 reinterpret_cast<SECURITY_DESCRIPTOR**>(
110 &security_attributes.lpSecurityDescriptor))) {
111 NOTREACHED();
112 }
113
114 pipe_ = CreateNamedPipeW(pipe_name.c_str(),
115 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
116 FILE_FLAG_FIRST_PIPE_INSTANCE,
117 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
118 1, // number of pipe instances
119 BUF_SIZE, // output buffer size (XXX tune)
120 BUF_SIZE, // input buffer size (XXX tune)
121 5000, // timeout in milliseconds (XXX tune)
122 &security_attributes);
123 LocalFree(security_attributes.lpSecurityDescriptor);
124 } else {
125 pipe_ = CreateFileW(pipe_name.c_str(),
126 GENERIC_READ | GENERIC_WRITE,
127 0,
128 NULL,
129 OPEN_EXISTING,
130 SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION |
131 FILE_FLAG_OVERLAPPED,
132 NULL);
133 }
134 if (pipe_ == INVALID_HANDLE_VALUE) {
135 // If this process is being closed, the pipe may be gone already.
136 LOG(WARNING) << "failed to create pipe: " << GetLastError();
137 return false;
138 }
139
140 // Create the Hello message to be sent when Connect is called
141 scoped_ptr<Message> m(new Message(MSG_ROUTING_NONE,
142 HELLO_MESSAGE_TYPE,
143 IPC::Message::PRIORITY_NORMAL));
144 if (!m->WriteInt(GetCurrentProcessId())) {
145 CloseHandle(pipe_);
146 pipe_ = INVALID_HANDLE_VALUE;
147 return false;
148 }
149
150 output_queue_.push(m.release());
151 return true;
152}
153
154bool Channel::Connect() {
155 DLOG(WARNING) << "Connect called twice";
156
157 if (pipe_ == INVALID_HANDLE_VALUE)
158 return false;
159
160 // Check to see if there is a client connected to our pipe...
161 if (waiting_connect_)
162 ProcessConnection();
163
164 if (!input_state_.is_pending) {
165 // complete setup asynchronously. to do that, we force the input state
166 // to be signaled, which will cause us to be called back from the event
167 // queue. by not setting input_state_.is_pending to true, we indicate
168 // to OnObjectSignaled that this is the special initialization signal.
169
170 SetEvent(input_state_.overlapped.hEvent);
[email protected]2de75522008-08-09 08:34:08171 MessageLoop::current()->WatchObject(input_state_.overlapped.hEvent, this);
initial.commit09911bf2008-07-26 23:55:29172 }
173
174 if (!waiting_connect_)
175 ProcessOutgoingMessages();
176 return true;
177}
178
179bool Channel::ProcessConnection() {
180 input_state_.is_pending = false;
[email protected]2de75522008-08-09 08:34:08181 MessageLoop::current()->WatchObject(input_state_.overlapped.hEvent, NULL);
initial.commit09911bf2008-07-26 23:55:29182
183 // Do we have a client connected to our pipe?
184 DCHECK(pipe_ != INVALID_HANDLE_VALUE);
185 BOOL ok = ConnectNamedPipe(pipe_, &input_state_.overlapped);
186
187 DWORD err = GetLastError();
188 if (ok) {
189 // Uhm, the API documentation says that this function should never
190 // return success when used in overlapped mode.
191 NOTREACHED();
192 return false;
193 }
194
195 switch (err) {
196 case ERROR_IO_PENDING:
197 input_state_.is_pending = true;
[email protected]2de75522008-08-09 08:34:08198 MessageLoop::current()->WatchObject(input_state_.overlapped.hEvent, this);
initial.commit09911bf2008-07-26 23:55:29199 break;
200 case ERROR_PIPE_CONNECTED:
201 waiting_connect_ = false;
202 break;
203 default:
204 NOTREACHED();
205 return false;
206 }
207
208 return true;
209}
210
211bool Channel::ProcessIncomingMessages() {
212 DWORD bytes_read = 0;
213
[email protected]2de75522008-08-09 08:34:08214 MessageLoop::current()->WatchObject(input_state_.overlapped.hEvent, NULL);
initial.commit09911bf2008-07-26 23:55:29215
216 if (input_state_.is_pending) {
217 input_state_.is_pending = false;
218
219 BOOL ok = GetOverlappedResult(pipe_,
220 &input_state_.overlapped,
221 &bytes_read,
222 FALSE);
223 if (!ok || bytes_read == 0) {
224 DWORD err = GetLastError();
225 // TODO(pkasting): http://b/119851 We can get ERR_BROKEN_PIPE here if the
226 // renderer crashes. We should handle this cleanly.
227 LOG(ERROR) << "pipe error: " << err;
228 return false;
229 }
230 } else {
231 // this happens at channel initialization
232 ResetEvent(input_state_.overlapped.hEvent);
233 }
234
235 for (;;) {
236 if (bytes_read == 0) {
237 // read from pipe...
238 BOOL ok = ReadFile(pipe_,
239 input_buf_,
240 BUF_SIZE,
241 &bytes_read,
242 &input_state_.overlapped);
243 if (!ok) {
244 DWORD err = GetLastError();
245 if (err == ERROR_IO_PENDING) {
[email protected]2de75522008-08-09 08:34:08246 MessageLoop::current()->WatchObject(input_state_.overlapped.hEvent,
247 this);
initial.commit09911bf2008-07-26 23:55:29248 input_state_.is_pending = true;
249 return true;
250 }
251 LOG(ERROR) << "pipe error: " << err;
252 return false;
253 }
254 }
255 DCHECK(bytes_read);
256
257 // process messages from input buffer
258
259 const char* p, *end;
260 if (input_overflow_buf_.empty()) {
261 p = input_buf_;
262 end = p + bytes_read;
263 } else {
264 if (input_overflow_buf_.size() > (kMaximumMessageSize - bytes_read)) {
265 input_overflow_buf_.clear();
266 LOG(ERROR) << "IPC message is too big";
267 return false;
268 }
269 input_overflow_buf_.append(input_buf_, bytes_read);
270 p = input_overflow_buf_.data();
271 end = p + input_overflow_buf_.size();
272 }
273
274 while (p < end) {
275 const char* message_tail = Message::FindNext(p, end);
276 if (message_tail) {
277 int len = static_cast<int>(message_tail - p);
278 const Message m(p, len);
279#ifdef IPC_MESSAGE_DEBUG_EXTRA
280 DLOG(INFO) << "received message on channel @" << this << " with type " <<
281 m.type();
282#endif
283 if (m.routing_id() == MSG_ROUTING_NONE &&
284 m.type() == HELLO_MESSAGE_TYPE) {
285 // The Hello message contains only the process id.
286 listener_->OnChannelConnected(MessageIterator(m).NextInt());
287 } else {
288 listener_->OnMessageReceived(m);
289 }
290 p = message_tail;
291 } else {
292 // last message is partial
293 break;
294 }
295 }
296 input_overflow_buf_.assign(p, end - p);
297
298 bytes_read = 0; // get more data
299 }
300
301 return true;
302}
303
304bool Channel::ProcessOutgoingMessages() {
305 DCHECK(!waiting_connect_); // Why are we trying to send messages if there's
306 // no connection?
307 DWORD bytes_written;
308
309 if (output_state_.is_pending) {
[email protected]2de75522008-08-09 08:34:08310 MessageLoop::current()->WatchObject(output_state_.overlapped.hEvent, NULL);
initial.commit09911bf2008-07-26 23:55:29311 output_state_.is_pending = false;
312 BOOL ok = GetOverlappedResult(pipe_,
313 &output_state_.overlapped,
314 &bytes_written,
315 FALSE);
316 if (!ok || bytes_written == 0) {
317 DWORD err = GetLastError();
318 LOG(ERROR) << "pipe error: " << err;
319 return false;
320 }
321 // message was sent
322 DCHECK(!output_queue_.empty());
323 Message* m = output_queue_.front();
324 output_queue_.pop();
325 delete m;
326 }
327
328 while (!output_queue_.empty()) {
329 // write to pipe...
330 Message* m = output_queue_.front();
331 BOOL ok = WriteFile(pipe_,
332 m->data(),
333 m->size(),
334 &bytes_written,
335 &output_state_.overlapped);
336 if (!ok) {
337 DWORD err = GetLastError();
338 if (err == ERROR_IO_PENDING) {
[email protected]2de75522008-08-09 08:34:08339 MessageLoop::current()->WatchObject(output_state_.overlapped.hEvent,
340 this);
initial.commit09911bf2008-07-26 23:55:29341 output_state_.is_pending = true;
342
343#ifdef IPC_MESSAGE_DEBUG_EXTRA
344 DLOG(INFO) << "sent pending message @" << m << " on channel @" << this <<
345 " with type " << m->type();
346#endif
347
348 return true;
349 }
350 LOG(ERROR) << "pipe error: " << err;
351 return false;
352 }
353 DCHECK(bytes_written == m->size());
354 output_queue_.pop();
355
356#ifdef IPC_MESSAGE_DEBUG_EXTRA
357 DLOG(INFO) << "sent message @" << m << " on channel @" << this <<
358 " with type " << m->type();
359#endif
360
361 delete m;
362 }
363
364 return true;
365}
366
[email protected]2de75522008-08-09 08:34:08367bool Channel::ProcessPendingMessages(DWORD max_wait_msec) {
368 return false;
369 // TODO(darin): this code is broken and leads to busy waiting
370#if 0
371 DCHECK(max_wait_msec <= 0x7FFFFFFF || max_wait_msec == INFINITE);
372
373 HANDLE events[] = {
374 input_state_.overlapped.hEvent,
375 output_state_.overlapped.hEvent
376 };
377 // Only deal with output messages if we have a connection on which to send
378 const int wait_count = waiting_connect_ ? 1 : 2;
379 DCHECK(wait_count <= _countof(events));
380
381 if (max_wait_msec) {
382 DWORD result = WaitForMultipleObjects(wait_count, events, FALSE,
383 max_wait_msec);
384 if (result == WAIT_TIMEOUT)
385 return true;
386 }
387
388 bool rv = true;
389 for (int i = 0; i < wait_count; ++i) {
390 if (WaitForSingleObject(events[i], 0) == WAIT_OBJECT_0) {
391 if (i == 0 && processing_incoming_) {
392 rv = false;
393 DLOG(WARNING) << "Would recurse into ProcessIncomingMessages";
394 } else {
395 OnObjectSignaled(events[i]);
396 }
397 }
398 }
399 return rv;
400#endif
401}
402
initial.commit09911bf2008-07-26 23:55:29403void Channel::OnObjectSignaled(HANDLE object) {
404 bool ok;
405 if (object == input_state_.overlapped.hEvent) {
406 if (waiting_connect_) {
407 ProcessConnection();
408 // We may have some messages queued up to send...
409 if (!output_queue_.empty() && !output_state_.is_pending)
410 ProcessOutgoingMessages();
411 if (input_state_.is_pending)
412 return;
413 // else, fall-through and look for incoming messages...
414 }
415 // we don't support recursion through OnMessageReceived yet!
416 DCHECK(!processing_incoming_);
417 processing_incoming_ = true;
418 ok = ProcessIncomingMessages();
419 processing_incoming_ = false;
420 } else {
421 DCHECK(object == output_state_.overlapped.hEvent);
422 ok = ProcessOutgoingMessages();
423 }
424 if (!ok) {
425 Close();
426 listener_->OnChannelError();
427 }
428}
429
430//------------------------------------------------------------------------------
431
432}
license.botbf09a502008-08-24 00:55:55433