• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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