1 /*
2 * Copyright (C) 2010 Apple Inc. All rights reserved.
3 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24 * THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "config.h"
28 #include "Connection.h"
29
30 #include "ArgumentEncoder.h"
31 #include "ProcessLauncher.h"
32 #include "WorkItem.h"
33 #include "SharedMemory.h"
34 #include "WebProcessProxy.h"
35 #include <sys/socket.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <wtf/Assertions.h>
40
41 #if PLATFORM(QT)
42 #include <QApplication>
43 #include <QSocketNotifier>
44 #elif PLATFORM(GTK)
45 #include <glib.h>
46 #endif
47
48 using namespace std;
49
50 namespace CoreIPC {
51
52 static const size_t messageMaxSize = 4096;
53 static const size_t attachmentMaxAmount = 255;
54
55 enum {
56 MessageBodyIsOOL = 1U << 31
57 };
58
59 class MessageInfo {
60 public:
MessageInfo()61 MessageInfo() { }
62
MessageInfo(MessageID messageID,size_t bodySize,size_t initialAttachmentCount)63 MessageInfo(MessageID messageID, size_t bodySize, size_t initialAttachmentCount)
64 : m_messageID(messageID.toInt())
65 , m_bodySize(bodySize)
66 , m_attachmentCount(initialAttachmentCount)
67 {
68 ASSERT(!(m_messageID & MessageBodyIsOOL));
69 }
70
setMessageBodyOOL()71 void setMessageBodyOOL()
72 {
73 ASSERT(!isMessageBodyOOL());
74
75 m_messageID |= MessageBodyIsOOL;
76 m_attachmentCount++;
77 }
78
isMessageBodyOOL() const79 bool isMessageBodyOOL() const { return m_messageID & MessageBodyIsOOL; }
80
bodySize() const81 size_t bodySize() const { return m_bodySize; }
82
messageID() const83 MessageID messageID() const { return MessageID::fromInt(m_messageID & ~MessageBodyIsOOL); }
84
attachmentCount() const85 size_t attachmentCount() const { return m_attachmentCount; }
86
87 private:
88 uint32_t m_messageID;
89 size_t m_bodySize;
90 size_t m_attachmentCount;
91 };
92
platformInitialize(Identifier identifier)93 void Connection::platformInitialize(Identifier identifier)
94 {
95 m_socketDescriptor = identifier;
96 m_readBuffer.resize(messageMaxSize);
97 m_currentMessageSize = 0;
98
99 #if PLATFORM(QT)
100 m_socketNotifier = 0;
101 #endif
102 }
103
platformInvalidate()104 void Connection::platformInvalidate()
105 {
106 if (m_socketDescriptor != -1)
107 while (close(m_socketDescriptor) == -1 && errno == EINTR) { }
108
109 if (!m_isConnected)
110 return;
111
112 #if PLATFORM(GTK)
113 m_connectionQueue.unregisterEventSourceHandler(m_socketDescriptor);
114 #endif
115
116 #if PLATFORM(QT)
117 delete m_socketNotifier;
118 m_socketNotifier = 0;
119 #endif
120
121 m_socketDescriptor = -1;
122 m_isConnected = false;
123 }
124
125 #if PLATFORM(QT)
126 class SocketNotifierResourceGuard {
127 public:
SocketNotifierResourceGuard(QSocketNotifier * socketNotifier)128 SocketNotifierResourceGuard(QSocketNotifier* socketNotifier)
129 : m_socketNotifier(socketNotifier)
130 {
131 m_socketNotifier->setEnabled(false);
132 }
133
~SocketNotifierResourceGuard()134 ~SocketNotifierResourceGuard()
135 {
136 m_socketNotifier->setEnabled(true);
137 }
138
139 private:
140 QSocketNotifier* const m_socketNotifier;
141 };
142 #endif
143
144 template<class T, class iterator>
145 class AttachmentResourceGuard {
146 public:
AttachmentResourceGuard(T & attachments)147 AttachmentResourceGuard(T& attachments)
148 : m_attachments(attachments)
149 {
150 }
~AttachmentResourceGuard()151 ~AttachmentResourceGuard()
152 {
153 iterator end = m_attachments.end();
154 for (iterator i = m_attachments.begin(); i != end; ++i)
155 i->dispose();
156 }
157 private:
158 T& m_attachments;
159 };
160
readyReadHandler()161 void Connection::readyReadHandler()
162 {
163 Deque<Attachment> attachments;
164 #if PLATFORM(QT)
165 SocketNotifierResourceGuard socketNotifierEnabler(m_socketNotifier);
166 #endif
167 AttachmentResourceGuard<Deque<Attachment>, Deque<Attachment>::iterator> attachementDisposer(attachments);
168
169 OwnArrayPtr<char> attachmentDescriptorBuffer = adoptArrayPtr(new char[CMSG_SPACE(sizeof(int) * (attachmentMaxAmount))]);
170 struct msghdr message;
171 memset(&message, 0, sizeof(message));
172
173 struct iovec iov[1];
174 memset(&iov, 0, sizeof(iov));
175
176 message.msg_control = attachmentDescriptorBuffer.get();
177 message.msg_controllen = CMSG_SPACE(sizeof(int) * (attachmentMaxAmount));
178
179 iov[0].iov_base = m_readBuffer.data();
180 iov[0].iov_len = m_readBuffer.size();
181
182 message.msg_iov = iov;
183 message.msg_iovlen = 1;
184
185
186 int messageLength = 0;
187 while ((messageLength = recvmsg(m_socketDescriptor, &message, 0)) == -1) {
188 if (errno != EINTR)
189 return;
190 }
191
192 struct cmsghdr* controlMessage = CMSG_FIRSTHDR(&message);
193
194 MessageInfo messageInfo;
195 unsigned char* messageData = m_readBuffer.data();
196
197 memcpy(&messageInfo, messageData, sizeof(messageInfo));
198 ASSERT(messageLength == sizeof(messageInfo) + messageInfo.attachmentCount() * sizeof(size_t) + (messageInfo.isMessageBodyOOL() ? 0 : messageInfo.bodySize()));
199
200 messageData += sizeof(messageInfo);
201
202 RefPtr<WebKit::SharedMemory> oolMessageBody;
203
204 if (messageInfo.attachmentCount()) {
205 if (controlMessage && controlMessage->cmsg_level == SOL_SOCKET && controlMessage->cmsg_type == SCM_RIGHTS) {
206 OwnArrayPtr<size_t> attachmentSizes = adoptArrayPtr(new size_t[messageInfo.attachmentCount()]);
207 memcpy(attachmentSizes.get(), messageData, sizeof(size_t) * messageInfo.attachmentCount());
208
209 messageData += sizeof(attachmentSizes);
210
211 OwnArrayPtr<int> fileDescriptors = adoptArrayPtr(new int[messageInfo.attachmentCount()]);
212 memcpy(fileDescriptors.get(), CMSG_DATA(controlMessage), sizeof(int) * messageInfo.attachmentCount());
213
214 int attachmentCount = messageInfo.attachmentCount();
215
216 if (messageInfo.isMessageBodyOOL())
217 attachmentCount--;
218
219 for (int i = 0; i < attachmentCount; ++i) {
220 while (fcntl(fileDescriptors[i], F_SETFL, FD_CLOEXEC) == -1) {
221 if (errno != EINTR) {
222 ASSERT_NOT_REACHED();
223 return;
224 }
225 }
226 }
227
228 for (int i = 0; i < attachmentCount; ++i)
229 attachments.append(Attachment(fileDescriptors[i], attachmentSizes[i]));
230
231 if (messageInfo.isMessageBodyOOL()) {
232 ASSERT(messageInfo.bodySize());
233
234 WebKit::SharedMemory::Handle handle;
235 handle.adoptFromAttachment(fileDescriptors[attachmentCount], attachmentSizes[attachmentCount]);
236 if (handle.isNull()) {
237 ASSERT_NOT_REACHED();
238 return;
239 }
240
241 oolMessageBody = WebKit::SharedMemory::create(handle, WebKit::SharedMemory::ReadOnly);
242 if (!oolMessageBody) {
243 ASSERT_NOT_REACHED();
244 return;
245 }
246 }
247
248 controlMessage = CMSG_NXTHDR(&message, controlMessage);
249 } else {
250 ASSERT_NOT_REACHED();
251 return;
252 }
253 }
254
255 ASSERT(attachments.size() == messageInfo.isMessageBodyOOL() ? messageInfo.attachmentCount() - 1 : messageInfo.attachmentCount());
256
257 unsigned char* messageBody = messageData;
258
259 if (messageInfo.isMessageBodyOOL())
260 messageBody = reinterpret_cast<unsigned char*>(oolMessageBody->data());
261
262 ArgumentDecoder* argumentDecoder;
263 if (attachments.isEmpty())
264 argumentDecoder = new ArgumentDecoder(messageBody, messageInfo.bodySize());
265 else
266 argumentDecoder = new ArgumentDecoder(messageBody, messageInfo.bodySize(), attachments);
267
268 processIncomingMessage(messageInfo.messageID(), adoptPtr(argumentDecoder));
269
270 ASSERT(!controlMessage);
271 }
272
open()273 bool Connection::open()
274 {
275 #if PLATFORM(QT)
276 ASSERT(!m_socketNotifier);
277 #endif
278
279 int flags = fcntl(m_socketDescriptor, F_GETFL, 0);
280 while (fcntl(m_socketDescriptor, F_SETFL, flags | O_NONBLOCK) == -1) {
281 if (errno != EINTR) {
282 ASSERT_NOT_REACHED();
283 return false;
284 }
285 }
286
287 m_isConnected = true;
288 #if PLATFORM(QT)
289 m_socketNotifier = m_connectionQueue.registerSocketEventHandler(m_socketDescriptor, QSocketNotifier::Read, WorkItem::create(this, &Connection::readyReadHandler));
290 #elif PLATFORM(GTK)
291 m_connectionQueue.registerEventSourceHandler(m_socketDescriptor, (G_IO_HUP | G_IO_ERR), WorkItem::create(this, &Connection::connectionDidClose));
292 m_connectionQueue.registerEventSourceHandler(m_socketDescriptor, G_IO_IN, WorkItem::create(this, &Connection::readyReadHandler));
293 #endif
294
295 // Schedule a call to readyReadHandler. Data may have arrived before installation of the signal
296 // handler.
297 m_connectionQueue.scheduleWork(WorkItem::create(this, &Connection::readyReadHandler));
298
299 return true;
300 }
301
platformCanSendOutgoingMessages() const302 bool Connection::platformCanSendOutgoingMessages() const
303 {
304 return m_isConnected;
305 }
306
sendOutgoingMessage(MessageID messageID,PassOwnPtr<ArgumentEncoder> arguments)307 bool Connection::sendOutgoingMessage(MessageID messageID, PassOwnPtr<ArgumentEncoder> arguments)
308 {
309 #if PLATFORM(QT)
310 ASSERT(m_socketNotifier);
311 #endif
312
313 COMPILE_ASSERT(sizeof(MessageInfo) + attachmentMaxAmount * sizeof(size_t) <= messageMaxSize, AttachmentsFitToMessageInline);
314
315 Vector<Attachment> attachments = arguments->releaseAttachments();
316 AttachmentResourceGuard<Vector<Attachment>, Vector<Attachment>::iterator> attachementDisposer(attachments);
317
318 if (attachments.size() > (attachmentMaxAmount - 1)) {
319 ASSERT_NOT_REACHED();
320 return false;
321 }
322
323 MessageInfo messageInfo(messageID, arguments->bufferSize(), attachments.size());
324 size_t messageSizeWithBodyInline = sizeof(messageInfo) + (attachments.size() * sizeof(size_t)) + arguments->bufferSize();
325 if (messageSizeWithBodyInline > messageMaxSize && arguments->bufferSize()) {
326 RefPtr<WebKit::SharedMemory> oolMessageBody = WebKit::SharedMemory::create(arguments->bufferSize());
327 if (!oolMessageBody)
328 return false;
329
330 WebKit::SharedMemory::Handle handle;
331 if (!oolMessageBody->createHandle(handle, WebKit::SharedMemory::ReadOnly))
332 return false;
333
334 messageInfo.setMessageBodyOOL();
335
336 memcpy(oolMessageBody->data(), arguments->buffer(), arguments->bufferSize());
337
338 attachments.append(handle.releaseToAttachment());
339 }
340
341 struct msghdr message;
342 memset(&message, 0, sizeof(message));
343
344 struct iovec iov[3];
345 memset(&iov, 0, sizeof(iov));
346
347 message.msg_iov = iov;
348 int iovLength = 1;
349
350 iov[0].iov_base = reinterpret_cast<void*>(&messageInfo);
351 iov[0].iov_len = sizeof(messageInfo);
352
353 OwnArrayPtr<char> attachmentFDBuffer = adoptArrayPtr(new char[CMSG_SPACE(sizeof(int) * attachments.size())]);
354 OwnArrayPtr<size_t> attachmentSizes = adoptArrayPtr(new size_t[attachments.size()]);
355
356 if (!attachments.isEmpty()) {
357 message.msg_control = attachmentFDBuffer.get();
358 message.msg_controllen = sizeof(char) * CMSG_SPACE(sizeof(int) * attachments.size());
359
360 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&message);
361 cmsg->cmsg_level = SOL_SOCKET;
362 cmsg->cmsg_type = SCM_RIGHTS;
363 cmsg->cmsg_len = CMSG_LEN(sizeof(int) * attachments.size());
364
365 int* fdptr = reinterpret_cast<int*>(CMSG_DATA(cmsg));
366 for (size_t i = 0; i < attachments.size(); ++i) {
367 attachmentSizes[i] = attachments[i].size();
368 fdptr[i] = attachments[i].fileDescriptor();
369 }
370
371 message.msg_controllen = cmsg->cmsg_len;
372
373 iov[iovLength].iov_base = attachmentSizes.get();
374 iov[iovLength].iov_len = sizeof(size_t) * attachments.size();
375 ++iovLength;
376 }
377
378 if (!messageInfo.isMessageBodyOOL() && arguments->bufferSize()) {
379 iov[iovLength].iov_base = reinterpret_cast<void*>(arguments->buffer());
380 iov[iovLength].iov_len = arguments->bufferSize();
381 ++iovLength;
382 }
383
384 message.msg_iovlen = iovLength;
385
386 int bytesSent = 0;
387 while ((bytesSent = sendmsg(m_socketDescriptor, &message, 0)) == -1) {
388 if (errno != EINTR)
389 return false;
390 }
391 return true;
392 }
393
394 #if PLATFORM(QT) || PLATFORM(GTK)
setShouldCloseConnectionOnProcessTermination(WebKit::PlatformProcessIdentifier process)395 void Connection::setShouldCloseConnectionOnProcessTermination(WebKit::PlatformProcessIdentifier process)
396 {
397 m_connectionQueue.scheduleWorkOnTermination(process, WorkItem::create(this, &Connection::connectionDidClose));
398 }
399 #endif
400
401 } // namespace CoreIPC
402