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