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
48using namespace std;
49
50namespace CoreIPC {
51
52static const size_t messageMaxSize = 4096;
53static const size_t attachmentMaxAmount = 255;
54
55enum {
56    MessageBodyIsOOL = 1U << 31
57};
58
59class MessageInfo {
60public:
61    MessageInfo() { }
62
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
71    void setMessageBodyOOL()
72    {
73        ASSERT(!isMessageBodyOOL());
74
75        m_messageID |= MessageBodyIsOOL;
76        m_attachmentCount++;
77    }
78
79    bool isMessageBodyOOL() const { return m_messageID & MessageBodyIsOOL; }
80
81    size_t bodySize() const { return m_bodySize; }
82
83    MessageID messageID() const { return MessageID::fromInt(m_messageID & ~MessageBodyIsOOL); }
84
85    size_t attachmentCount() const { return m_attachmentCount; }
86
87private:
88    uint32_t m_messageID;
89    size_t m_bodySize;
90    size_t m_attachmentCount;
91};
92
93void 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
104void 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)
126class SocketNotifierResourceGuard {
127public:
128    SocketNotifierResourceGuard(QSocketNotifier* socketNotifier)
129        : m_socketNotifier(socketNotifier)
130    {
131        m_socketNotifier->setEnabled(false);
132    }
133
134    ~SocketNotifierResourceGuard()
135    {
136        m_socketNotifier->setEnabled(true);
137    }
138
139private:
140    QSocketNotifier* const m_socketNotifier;
141};
142#endif
143
144template<class T, class iterator>
145class AttachmentResourceGuard {
146public:
147    AttachmentResourceGuard(T& attachments)
148        : m_attachments(attachments)
149    {
150    }
151    ~AttachmentResourceGuard()
152    {
153        iterator end = m_attachments.end();
154        for (iterator i = m_attachments.begin(); i != end; ++i)
155            i->dispose();
156    }
157private:
158    T& m_attachments;
159};
160
161void 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
273bool 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
302bool Connection::platformCanSendOutgoingMessages() const
303{
304    return m_isConnected;
305}
306
307bool 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)
395void Connection::setShouldCloseConnectionOnProcessTermination(WebKit::PlatformProcessIdentifier process)
396{
397    m_connectionQueue.scheduleWorkOnTermination(process, WorkItem::create(this, &Connection::connectionDidClose));
398}
399#endif
400
401} // namespace CoreIPC
402