/* * Copyright (C) 2010 Apple Inc. All rights reserved. * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "Connection.h" #include "ArgumentEncoder.h" #include "ProcessLauncher.h" #include "WorkItem.h" #include "SharedMemory.h" #include "WebProcessProxy.h" #include #include #include #include #include #if PLATFORM(QT) #include #include #elif PLATFORM(GTK) #include #endif using namespace std; namespace CoreIPC { static const size_t messageMaxSize = 4096; static const size_t attachmentMaxAmount = 255; enum { MessageBodyIsOOL = 1U << 31 }; class MessageInfo { public: MessageInfo() { } MessageInfo(MessageID messageID, size_t bodySize, size_t initialAttachmentCount) : m_messageID(messageID.toInt()) , m_bodySize(bodySize) , m_attachmentCount(initialAttachmentCount) { ASSERT(!(m_messageID & MessageBodyIsOOL)); } void setMessageBodyOOL() { ASSERT(!isMessageBodyOOL()); m_messageID |= MessageBodyIsOOL; m_attachmentCount++; } bool isMessageBodyOOL() const { return m_messageID & MessageBodyIsOOL; } size_t bodySize() const { return m_bodySize; } MessageID messageID() const { return MessageID::fromInt(m_messageID & ~MessageBodyIsOOL); } size_t attachmentCount() const { return m_attachmentCount; } private: uint32_t m_messageID; size_t m_bodySize; size_t m_attachmentCount; }; void Connection::platformInitialize(Identifier identifier) { m_socketDescriptor = identifier; m_readBuffer.resize(messageMaxSize); m_currentMessageSize = 0; #if PLATFORM(QT) m_socketNotifier = 0; #endif } void Connection::platformInvalidate() { if (m_socketDescriptor != -1) while (close(m_socketDescriptor) == -1 && errno == EINTR) { } if (!m_isConnected) return; #if PLATFORM(GTK) m_connectionQueue.unregisterEventSourceHandler(m_socketDescriptor); #endif #if PLATFORM(QT) delete m_socketNotifier; m_socketNotifier = 0; #endif m_socketDescriptor = -1; m_isConnected = false; } #if PLATFORM(QT) class SocketNotifierResourceGuard { public: SocketNotifierResourceGuard(QSocketNotifier* socketNotifier) : m_socketNotifier(socketNotifier) { m_socketNotifier->setEnabled(false); } ~SocketNotifierResourceGuard() { m_socketNotifier->setEnabled(true); } private: QSocketNotifier* const m_socketNotifier; }; #endif template class AttachmentResourceGuard { public: AttachmentResourceGuard(T& attachments) : m_attachments(attachments) { } ~AttachmentResourceGuard() { iterator end = m_attachments.end(); for (iterator i = m_attachments.begin(); i != end; ++i) i->dispose(); } private: T& m_attachments; }; void Connection::readyReadHandler() { Deque attachments; #if PLATFORM(QT) SocketNotifierResourceGuard socketNotifierEnabler(m_socketNotifier); #endif AttachmentResourceGuard, Deque::iterator> attachementDisposer(attachments); OwnArrayPtr attachmentDescriptorBuffer = adoptArrayPtr(new char[CMSG_SPACE(sizeof(int) * (attachmentMaxAmount))]); struct msghdr message; memset(&message, 0, sizeof(message)); struct iovec iov[1]; memset(&iov, 0, sizeof(iov)); message.msg_control = attachmentDescriptorBuffer.get(); message.msg_controllen = CMSG_SPACE(sizeof(int) * (attachmentMaxAmount)); iov[0].iov_base = m_readBuffer.data(); iov[0].iov_len = m_readBuffer.size(); message.msg_iov = iov; message.msg_iovlen = 1; int messageLength = 0; while ((messageLength = recvmsg(m_socketDescriptor, &message, 0)) == -1) { if (errno != EINTR) return; } struct cmsghdr* controlMessage = CMSG_FIRSTHDR(&message); MessageInfo messageInfo; unsigned char* messageData = m_readBuffer.data(); memcpy(&messageInfo, messageData, sizeof(messageInfo)); ASSERT(messageLength == sizeof(messageInfo) + messageInfo.attachmentCount() * sizeof(size_t) + (messageInfo.isMessageBodyOOL() ? 0 : messageInfo.bodySize())); messageData += sizeof(messageInfo); RefPtr oolMessageBody; if (messageInfo.attachmentCount()) { if (controlMessage && controlMessage->cmsg_level == SOL_SOCKET && controlMessage->cmsg_type == SCM_RIGHTS) { OwnArrayPtr attachmentSizes = adoptArrayPtr(new size_t[messageInfo.attachmentCount()]); memcpy(attachmentSizes.get(), messageData, sizeof(size_t) * messageInfo.attachmentCount()); messageData += sizeof(attachmentSizes); OwnArrayPtr fileDescriptors = adoptArrayPtr(new int[messageInfo.attachmentCount()]); memcpy(fileDescriptors.get(), CMSG_DATA(controlMessage), sizeof(int) * messageInfo.attachmentCount()); int attachmentCount = messageInfo.attachmentCount(); if (messageInfo.isMessageBodyOOL()) attachmentCount--; for (int i = 0; i < attachmentCount; ++i) { while (fcntl(fileDescriptors[i], F_SETFL, FD_CLOEXEC) == -1) { if (errno != EINTR) { ASSERT_NOT_REACHED(); return; } } } for (int i = 0; i < attachmentCount; ++i) attachments.append(Attachment(fileDescriptors[i], attachmentSizes[i])); if (messageInfo.isMessageBodyOOL()) { ASSERT(messageInfo.bodySize()); WebKit::SharedMemory::Handle handle; handle.adoptFromAttachment(fileDescriptors[attachmentCount], attachmentSizes[attachmentCount]); if (handle.isNull()) { ASSERT_NOT_REACHED(); return; } oolMessageBody = WebKit::SharedMemory::create(handle, WebKit::SharedMemory::ReadOnly); if (!oolMessageBody) { ASSERT_NOT_REACHED(); return; } } controlMessage = CMSG_NXTHDR(&message, controlMessage); } else { ASSERT_NOT_REACHED(); return; } } ASSERT(attachments.size() == messageInfo.isMessageBodyOOL() ? messageInfo.attachmentCount() - 1 : messageInfo.attachmentCount()); unsigned char* messageBody = messageData; if (messageInfo.isMessageBodyOOL()) messageBody = reinterpret_cast(oolMessageBody->data()); ArgumentDecoder* argumentDecoder; if (attachments.isEmpty()) argumentDecoder = new ArgumentDecoder(messageBody, messageInfo.bodySize()); else argumentDecoder = new ArgumentDecoder(messageBody, messageInfo.bodySize(), attachments); processIncomingMessage(messageInfo.messageID(), adoptPtr(argumentDecoder)); ASSERT(!controlMessage); } bool Connection::open() { #if PLATFORM(QT) ASSERT(!m_socketNotifier); #endif int flags = fcntl(m_socketDescriptor, F_GETFL, 0); while (fcntl(m_socketDescriptor, F_SETFL, flags | O_NONBLOCK) == -1) { if (errno != EINTR) { ASSERT_NOT_REACHED(); return false; } } m_isConnected = true; #if PLATFORM(QT) m_socketNotifier = m_connectionQueue.registerSocketEventHandler(m_socketDescriptor, QSocketNotifier::Read, WorkItem::create(this, &Connection::readyReadHandler)); #elif PLATFORM(GTK) m_connectionQueue.registerEventSourceHandler(m_socketDescriptor, (G_IO_HUP | G_IO_ERR), WorkItem::create(this, &Connection::connectionDidClose)); m_connectionQueue.registerEventSourceHandler(m_socketDescriptor, G_IO_IN, WorkItem::create(this, &Connection::readyReadHandler)); #endif // Schedule a call to readyReadHandler. Data may have arrived before installation of the signal // handler. m_connectionQueue.scheduleWork(WorkItem::create(this, &Connection::readyReadHandler)); return true; } bool Connection::platformCanSendOutgoingMessages() const { return m_isConnected; } bool Connection::sendOutgoingMessage(MessageID messageID, PassOwnPtr arguments) { #if PLATFORM(QT) ASSERT(m_socketNotifier); #endif COMPILE_ASSERT(sizeof(MessageInfo) + attachmentMaxAmount * sizeof(size_t) <= messageMaxSize, AttachmentsFitToMessageInline); Vector attachments = arguments->releaseAttachments(); AttachmentResourceGuard, Vector::iterator> attachementDisposer(attachments); if (attachments.size() > (attachmentMaxAmount - 1)) { ASSERT_NOT_REACHED(); return false; } MessageInfo messageInfo(messageID, arguments->bufferSize(), attachments.size()); size_t messageSizeWithBodyInline = sizeof(messageInfo) + (attachments.size() * sizeof(size_t)) + arguments->bufferSize(); if (messageSizeWithBodyInline > messageMaxSize && arguments->bufferSize()) { RefPtr oolMessageBody = WebKit::SharedMemory::create(arguments->bufferSize()); if (!oolMessageBody) return false; WebKit::SharedMemory::Handle handle; if (!oolMessageBody->createHandle(handle, WebKit::SharedMemory::ReadOnly)) return false; messageInfo.setMessageBodyOOL(); memcpy(oolMessageBody->data(), arguments->buffer(), arguments->bufferSize()); attachments.append(handle.releaseToAttachment()); } struct msghdr message; memset(&message, 0, sizeof(message)); struct iovec iov[3]; memset(&iov, 0, sizeof(iov)); message.msg_iov = iov; int iovLength = 1; iov[0].iov_base = reinterpret_cast(&messageInfo); iov[0].iov_len = sizeof(messageInfo); OwnArrayPtr attachmentFDBuffer = adoptArrayPtr(new char[CMSG_SPACE(sizeof(int) * attachments.size())]); OwnArrayPtr attachmentSizes = adoptArrayPtr(new size_t[attachments.size()]); if (!attachments.isEmpty()) { message.msg_control = attachmentFDBuffer.get(); message.msg_controllen = sizeof(char) * CMSG_SPACE(sizeof(int) * attachments.size()); struct cmsghdr* cmsg = CMSG_FIRSTHDR(&message); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(int) * attachments.size()); int* fdptr = reinterpret_cast(CMSG_DATA(cmsg)); for (size_t i = 0; i < attachments.size(); ++i) { attachmentSizes[i] = attachments[i].size(); fdptr[i] = attachments[i].fileDescriptor(); } message.msg_controllen = cmsg->cmsg_len; iov[iovLength].iov_base = attachmentSizes.get(); iov[iovLength].iov_len = sizeof(size_t) * attachments.size(); ++iovLength; } if (!messageInfo.isMessageBodyOOL() && arguments->bufferSize()) { iov[iovLength].iov_base = reinterpret_cast(arguments->buffer()); iov[iovLength].iov_len = arguments->bufferSize(); ++iovLength; } message.msg_iovlen = iovLength; int bytesSent = 0; while ((bytesSent = sendmsg(m_socketDescriptor, &message, 0)) == -1) { if (errno != EINTR) return false; } return true; } #if PLATFORM(QT) || PLATFORM(GTK) void Connection::setShouldCloseConnectionOnProcessTermination(WebKit::PlatformProcessIdentifier process) { m_connectionQueue.scheduleWorkOnTermination(process, WorkItem::create(this, &Connection::connectionDidClose)); } #endif } // namespace CoreIPC