Home | History | Annotate | Download | only in unix
      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:
     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 
     87 private:
     88     uint32_t m_messageID;
     89     size_t m_bodySize;
     90     size_t m_attachmentCount;
     91 };
     92 
     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 
    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:
    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 
    139 private:
    140     QSocketNotifier* const m_socketNotifier;
    141 };
    142 #endif
    143 
    144 template<class T, class iterator>
    145 class AttachmentResourceGuard {
    146 public:
    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     }
    157 private:
    158     T& m_attachments;
    159 };
    160 
    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 
    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 
    302 bool Connection::platformCanSendOutgoingMessages() const
    303 {
    304     return m_isConnected;
    305 }
    306 
    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)
    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