blob: d469736268e39446f10664ff226b5129f1afaa2a [file] [log] [blame]
initial.commit09911bf2008-07-26 23:55:291// Copyright 2008, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#include <windows.h>
31#include <sstream>
32
33#include "chrome/common/chrome_counters.h"
34#include "chrome/common/ipc_channel.h"
35
36#include "base/logging.h"
37#include "base/win_util.h"
38#include "chrome/common/ipc_logging.h"
39#include "chrome/common/ipc_message_utils.h"
40
41using namespace std;
42
43namespace IPC {
44
45//------------------------------------------------------------------------------
46
47Channel::State::State()
48 : is_pending(false) {
49 memset(&overlapped, 0, sizeof(overlapped));
50 overlapped.hEvent = CreateEvent(NULL, // default security attributes
51 TRUE, // manual-reset event
52 TRUE, // initial state = signaled
53 NULL); // unnamed event object
54}
55
56Channel::State::~State() {
57 if (overlapped.hEvent)
58 CloseHandle(overlapped.hEvent);
59}
60
61//------------------------------------------------------------------------------
62
63Channel::Channel(const wstring& channel_id, Mode mode, Listener* listener)
64 : pipe_(INVALID_HANDLE_VALUE),
65 listener_(listener),
66 waiting_connect_(mode == MODE_SERVER),
67 processing_incoming_(false) {
68 if (!CreatePipe(channel_id, mode)) {
69 // The pipe may have been closed already.
70 LOG(WARNING) << "Unable to create pipe named \"" << channel_id <<
71 "\" in " << (mode == 0 ? "server" : "client") << " mode.";
72 }
73}
74
75void Channel::Close() {
76 // make sure we are no longer watching the pipe events
77 MessageLoop::current()->WatchObject(input_state_.overlapped.hEvent, NULL);
78 MessageLoop::current()->WatchObject(output_state_.overlapped.hEvent, NULL);
79
80 if (pipe_ != INVALID_HANDLE_VALUE) {
81 CloseHandle(pipe_);
82 pipe_ = INVALID_HANDLE_VALUE;
83 }
84
85 while (!output_queue_.empty()) {
86 Message* m = output_queue_.front();
87 output_queue_.pop();
88 delete m;
89 }
90}
91
92bool Channel::Send(Message* message) {
93 chrome::Counters::ipc_send_counter().Increment();
94#ifdef IPC_MESSAGE_DEBUG_EXTRA
95 DLOG(INFO) << "sending message @" << message << " on channel @" << this
96 << " with type " << message->type()
97 << " (" << output_queue_.size() << " in queue)";
98#endif
99
100#ifdef IPC_MESSAGE_LOG_ENABLED
101 Logging::current()->OnSendMessage(message, L"");
102#endif
103
104 output_queue_.push(message);
105 // ensure waiting to write
106 if (!waiting_connect_) {
107 if (!output_state_.is_pending) {
108 if (!ProcessOutgoingMessages())
109 return false;
110 } else if (WaitForSingleObject(output_state_.overlapped.hEvent, 0) ==
111 WAIT_OBJECT_0) {
112 OnObjectSignaled(output_state_.overlapped.hEvent);
113 }
114 }
115
116 return true;
117}
118
119const wstring Channel::PipeName(const wstring& channel_id) const {
120 wostringstream ss;
121 // XXX(darin): get application name from somewhere else
122 ss << L"\\\\.\\pipe\\chrome." << channel_id;
123 return ss.str();
124}
125
126bool Channel::CreatePipe(const wstring& channel_id, Mode mode) {
127 DCHECK(pipe_ == INVALID_HANDLE_VALUE);
128 const wstring pipe_name = PipeName(channel_id);
129 if (mode == MODE_SERVER) {
130 SECURITY_ATTRIBUTES security_attributes = {0};
131 security_attributes.bInheritHandle = FALSE;
132 security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
133 if (!win_util::GetLogonSessionOnlyDACL(
134 reinterpret_cast<SECURITY_DESCRIPTOR**>(
135 &security_attributes.lpSecurityDescriptor))) {
136 NOTREACHED();
137 }
138
139 pipe_ = CreateNamedPipeW(pipe_name.c_str(),
140 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
141 FILE_FLAG_FIRST_PIPE_INSTANCE,
142 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
143 1, // number of pipe instances
144 BUF_SIZE, // output buffer size (XXX tune)
145 BUF_SIZE, // input buffer size (XXX tune)
146 5000, // timeout in milliseconds (XXX tune)
147 &security_attributes);
148 LocalFree(security_attributes.lpSecurityDescriptor);
149 } else {
150 pipe_ = CreateFileW(pipe_name.c_str(),
151 GENERIC_READ | GENERIC_WRITE,
152 0,
153 NULL,
154 OPEN_EXISTING,
155 SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION |
156 FILE_FLAG_OVERLAPPED,
157 NULL);
158 }
159 if (pipe_ == INVALID_HANDLE_VALUE) {
160 // If this process is being closed, the pipe may be gone already.
161 LOG(WARNING) << "failed to create pipe: " << GetLastError();
162 return false;
163 }
164
165 // Create the Hello message to be sent when Connect is called
166 scoped_ptr<Message> m(new Message(MSG_ROUTING_NONE,
167 HELLO_MESSAGE_TYPE,
168 IPC::Message::PRIORITY_NORMAL));
169 if (!m->WriteInt(GetCurrentProcessId())) {
170 CloseHandle(pipe_);
171 pipe_ = INVALID_HANDLE_VALUE;
172 return false;
173 }
174
175 output_queue_.push(m.release());
176 return true;
177}
178
179bool Channel::Connect() {
180 DLOG(WARNING) << "Connect called twice";
181
182 if (pipe_ == INVALID_HANDLE_VALUE)
183 return false;
184
185 // 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) {
190 // complete setup asynchronously. to do that, we force the input state
191 // to be signaled, which will cause us to be called back from the event
192 // queue. by not setting input_state_.is_pending to true, we indicate
193 // to OnObjectSignaled that this is the special initialization signal.
194
195 SetEvent(input_state_.overlapped.hEvent);
196 MessageLoop::current()->WatchObject(input_state_.overlapped.hEvent, this);
197 }
198
199 if (!waiting_connect_)
200 ProcessOutgoingMessages();
201 return true;
202}
203
204bool Channel::ProcessConnection() {
205 input_state_.is_pending = false;
206 MessageLoop::current()->WatchObject(input_state_.overlapped.hEvent, NULL);
207
208 // Do we have a client connected to our pipe?
209 DCHECK(pipe_ != INVALID_HANDLE_VALUE);
210 BOOL ok = ConnectNamedPipe(pipe_, &input_state_.overlapped);
211
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;
223 MessageLoop::current()->WatchObject(input_state_.overlapped.hEvent, this);
224 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
236bool Channel::ProcessIncomingMessages() {
237 DWORD bytes_read = 0;
238
239 MessageLoop::current()->WatchObject(input_state_.overlapped.hEvent, NULL);
240
241 if (input_state_.is_pending) {
242 input_state_.is_pending = false;
243
244 BOOL ok = GetOverlappedResult(pipe_,
245 &input_state_.overlapped,
246 &bytes_read,
247 FALSE);
248 if (!ok || bytes_read == 0) {
249 DWORD err = GetLastError();
250 // TODO(pkasting): http://b/119851 We can get ERR_BROKEN_PIPE here if the
251 // renderer crashes. We should handle this cleanly.
252 LOG(ERROR) << "pipe error: " << err;
253 return false;
254 }
255 } else {
256 // this happens at channel initialization
257 ResetEvent(input_state_.overlapped.hEvent);
258 }
259
260 for (;;) {
261 if (bytes_read == 0) {
262 // read from pipe...
263 BOOL ok = ReadFile(pipe_,
264 input_buf_,
265 BUF_SIZE,
266 &bytes_read,
267 &input_state_.overlapped);
268 if (!ok) {
269 DWORD err = GetLastError();
270 if (err == ERROR_IO_PENDING) {
271 MessageLoop::current()->WatchObject(input_state_.overlapped.hEvent,
272 this);
273 input_state_.is_pending = true;
274 return true;
275 }
276 LOG(ERROR) << "pipe error: " << err;
277 return false;
278 }
279 }
280 DCHECK(bytes_read);
281
282 // process messages from input buffer
283
284 const char* p, *end;
285 if (input_overflow_buf_.empty()) {
286 p = input_buf_;
287 end = p + bytes_read;
288 } else {
289 if (input_overflow_buf_.size() > (kMaximumMessageSize - bytes_read)) {
290 input_overflow_buf_.clear();
291 LOG(ERROR) << "IPC message is too big";
292 return false;
293 }
294 input_overflow_buf_.append(input_buf_, bytes_read);
295 p = input_overflow_buf_.data();
296 end = p + input_overflow_buf_.size();
297 }
298
299 while (p < end) {
300 const char* message_tail = Message::FindNext(p, end);
301 if (message_tail) {
302 int len = static_cast<int>(message_tail - p);
303 const Message m(p, len);
304#ifdef IPC_MESSAGE_DEBUG_EXTRA
305 DLOG(INFO) << "received message on channel @" << this << " with type " <<
306 m.type();
307#endif
308 if (m.routing_id() == MSG_ROUTING_NONE &&
309 m.type() == HELLO_MESSAGE_TYPE) {
310 // The Hello message contains only the process id.
311 listener_->OnChannelConnected(MessageIterator(m).NextInt());
312 } else {
313 listener_->OnMessageReceived(m);
314 }
315 p = message_tail;
316 } else {
317 // last message is partial
318 break;
319 }
320 }
321 input_overflow_buf_.assign(p, end - p);
322
323 bytes_read = 0; // get more data
324 }
325
326 return true;
327}
328
329bool Channel::ProcessOutgoingMessages() {
330 DCHECK(!waiting_connect_); // Why are we trying to send messages if there's
331 // no connection?
332 DWORD bytes_written;
333
334 if (output_state_.is_pending) {
335 MessageLoop::current()->WatchObject(output_state_.overlapped.hEvent, NULL);
336 output_state_.is_pending = false;
337 BOOL ok = GetOverlappedResult(pipe_,
338 &output_state_.overlapped,
339 &bytes_written,
340 FALSE);
341 if (!ok || bytes_written == 0) {
342 DWORD err = GetLastError();
343 LOG(ERROR) << "pipe error: " << err;
344 return false;
345 }
346 // message was sent
347 DCHECK(!output_queue_.empty());
348 Message* m = output_queue_.front();
349 output_queue_.pop();
350 delete m;
351 }
352
353 while (!output_queue_.empty()) {
354 // write to pipe...
355 Message* m = output_queue_.front();
356 BOOL ok = WriteFile(pipe_,
357 m->data(),
358 m->size(),
359 &bytes_written,
360 &output_state_.overlapped);
361 if (!ok) {
362 DWORD err = GetLastError();
363 if (err == ERROR_IO_PENDING) {
364 MessageLoop::current()->WatchObject(output_state_.overlapped.hEvent,
365 this);
366 output_state_.is_pending = true;
367
368#ifdef IPC_MESSAGE_DEBUG_EXTRA
369 DLOG(INFO) << "sent pending message @" << m << " on channel @" << this <<
370 " with type " << m->type();
371#endif
372
373 return true;
374 }
375 LOG(ERROR) << "pipe error: " << err;
376 return false;
377 }
378 DCHECK(bytes_written == m->size());
379 output_queue_.pop();
380
381#ifdef IPC_MESSAGE_DEBUG_EXTRA
382 DLOG(INFO) << "sent message @" << m << " on channel @" << this <<
383 " with type " << m->type();
384#endif
385
386 delete m;
387 }
388
389 return true;
390}
391
392bool Channel::ProcessPendingMessages(DWORD max_wait_msec) {
393 return false;
394 // TODO(darin): this code is broken and leads to busy waiting
395#if 0
396 DCHECK(max_wait_msec <= 0x7FFFFFFF || max_wait_msec == INFINITE);
397
398 HANDLE events[] = {
399 input_state_.overlapped.hEvent,
400 output_state_.overlapped.hEvent
401 };
402 // Only deal with output messages if we have a connection on which to send
403 const int wait_count = waiting_connect_ ? 1 : 2;
404 DCHECK(wait_count <= _countof(events));
405
406 if (max_wait_msec) {
407 DWORD result = WaitForMultipleObjects(wait_count, events, FALSE,
408 max_wait_msec);
409 if (result == WAIT_TIMEOUT)
410 return true;
411 }
412
413 bool rv = true;
414 for (int i = 0; i < wait_count; ++i) {
415 if (WaitForSingleObject(events[i], 0) == WAIT_OBJECT_0) {
416 if (i == 0 && processing_incoming_) {
417 rv = false;
418 DLOG(WARNING) << "Would recurse into ProcessIncomingMessages";
419 } else {
420 OnObjectSignaled(events[i]);
421 }
422 }
423 }
424 return rv;
425#endif
426}
427
428void Channel::OnObjectSignaled(HANDLE object) {
429 bool ok;
430 if (object == input_state_.overlapped.hEvent) {
431 if (waiting_connect_) {
432 ProcessConnection();
433 // We may have some messages queued up to send...
434 if (!output_queue_.empty() && !output_state_.is_pending)
435 ProcessOutgoingMessages();
436 if (input_state_.is_pending)
437 return;
438 // else, fall-through and look for incoming messages...
439 }
440 // we don't support recursion through OnMessageReceived yet!
441 DCHECK(!processing_incoming_);
442 processing_incoming_ = true;
443 ok = ProcessIncomingMessages();
444 processing_incoming_ = false;
445 } else {
446 DCHECK(object == output_state_.overlapped.hEvent);
447 ok = ProcessOutgoingMessages();
448 }
449 if (!ok) {
450 Close();
451 listener_->OnChannelError();
452 }
453}
454
455//------------------------------------------------------------------------------
456
457}