1/* 2 * Copyright (C) 2010 Apple Inc. All rights reserved. 3 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) 4 * Copyright (C) 2011 Igalia S.L. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 19 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 25 * THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#include "config.h" 29#include "Connection.h" 30 31#include "DataReference.h" 32#include "SharedMemory.h" 33#include <sys/socket.h> 34#include <unistd.h> 35#include <errno.h> 36#include <fcntl.h> 37#include <wtf/Assertions.h> 38#include <wtf/Functional.h> 39#include <wtf/OwnArrayPtr.h> 40 41#if PLATFORM(QT) 42#include <QPointer> 43#include <QSocketNotifier> 44#elif PLATFORM(GTK) 45#include <glib.h> 46#endif 47 48namespace CoreIPC { 49 50static const size_t messageMaxSize = 4096; 51static const size_t attachmentMaxAmount = 255; 52 53enum { 54 MessageBodyIsOutOfLine = 1U << 31 55}; 56 57class MessageInfo { 58public: 59 MessageInfo() { } 60 61 MessageInfo(size_t bodySize, size_t initialAttachmentCount) 62 : m_bodySize(bodySize) 63 , m_attachmentCount(initialAttachmentCount) 64 , m_isMessageBodyOutOfLine(false) 65 { 66 } 67 68 void setMessageBodyIsOutOfLine() 69 { 70 ASSERT(!isMessageBodyIsOutOfLine()); 71 72 m_isMessageBodyOutOfLine = true; 73 m_attachmentCount++; 74 } 75 76 bool isMessageBodyIsOutOfLine() const { return m_isMessageBodyOutOfLine; } 77 78 size_t bodySize() const { return m_bodySize; } 79 80 size_t attachmentCount() const { return m_attachmentCount; } 81 82private: 83 size_t m_bodySize; 84 size_t m_attachmentCount; 85 bool m_isMessageBodyOutOfLine; 86}; 87 88class AttachmentInfo { 89public: 90 AttachmentInfo() 91 : m_type(Attachment::Uninitialized) 92 , m_size(0) 93 , m_isNull(false) 94 { 95 } 96 97 void setType(Attachment::Type type) { m_type = type; } 98 Attachment::Type getType() { return m_type; } 99 void setSize(size_t size) 100 { 101 ASSERT(m_type == Attachment::MappedMemoryType); 102 m_size = size; 103 } 104 105 size_t getSize() 106 { 107 ASSERT(m_type == Attachment::MappedMemoryType); 108 return m_size; 109 } 110 111 // The attachment is not null unless explicitly set. 112 void setNull() { m_isNull = true; } 113 bool isNull() { return m_isNull; } 114 115private: 116 Attachment::Type m_type; 117 size_t m_size; 118 bool m_isNull; 119}; 120 121void Connection::platformInitialize(Identifier identifier) 122{ 123 m_socketDescriptor = identifier; 124 m_readBuffer.resize(messageMaxSize); 125 m_readBufferSize = 0; 126 m_fileDescriptors.resize(attachmentMaxAmount); 127 m_fileDescriptorsSize = 0; 128 129#if PLATFORM(QT) 130 m_socketNotifier = 0; 131#endif 132} 133 134void Connection::platformInvalidate() 135{ 136 // In GTK+ platform the socket is closed by the work queue. 137#if !PLATFORM(GTK) 138 if (m_socketDescriptor != -1) 139 while (close(m_socketDescriptor) == -1 && errno == EINTR) { } 140#endif 141 142 if (!m_isConnected) 143 return; 144 145#if PLATFORM(GTK) 146 m_connectionQueue->unregisterSocketEventHandler(m_socketDescriptor); 147#endif 148 149#if PLATFORM(QT) 150 delete m_socketNotifier; 151 m_socketNotifier = 0; 152#endif 153 154#if PLATFORM(EFL) 155 m_connectionQueue->unregisterSocketEventHandler(m_socketDescriptor); 156#endif 157 158 m_socketDescriptor = -1; 159 m_isConnected = false; 160} 161 162#if PLATFORM(QT) 163class SocketNotifierResourceGuard { 164public: 165 SocketNotifierResourceGuard(QSocketNotifier* socketNotifier) 166 : m_socketNotifier(socketNotifier) 167 { 168 m_socketNotifier.data()->setEnabled(false); 169 } 170 171 ~SocketNotifierResourceGuard() 172 { 173 if (m_socketNotifier) 174 m_socketNotifier.data()->setEnabled(true); 175 } 176 177private: 178 QPointer<QSocketNotifier> const m_socketNotifier; 179}; 180#endif 181 182template<class T, class iterator> 183class AttachmentResourceGuard { 184public: 185 AttachmentResourceGuard(T& attachments) 186 : m_attachments(attachments) 187 { 188 } 189 ~AttachmentResourceGuard() 190 { 191 iterator end = m_attachments.end(); 192 for (iterator i = m_attachments.begin(); i != end; ++i) 193 i->dispose(); 194 } 195private: 196 T& m_attachments; 197}; 198 199bool Connection::processMessage() 200{ 201 if (m_readBufferSize < sizeof(MessageInfo)) 202 return false; 203 204 uint8_t* messageData = m_readBuffer.data(); 205 MessageInfo messageInfo; 206 memcpy(&messageInfo, messageData, sizeof(messageInfo)); 207 messageData += sizeof(messageInfo); 208 209 size_t messageLength = sizeof(MessageInfo) + messageInfo.attachmentCount() * sizeof(AttachmentInfo) + (messageInfo.isMessageBodyIsOutOfLine() ? 0 : messageInfo.bodySize()); 210 if (m_readBufferSize < messageLength) 211 return false; 212 213 size_t attachmentFileDescriptorCount = 0; 214 size_t attachmentCount = messageInfo.attachmentCount(); 215 OwnArrayPtr<AttachmentInfo> attachmentInfo; 216 217 if (attachmentCount) { 218 attachmentInfo = adoptArrayPtr(new AttachmentInfo[attachmentCount]); 219 memcpy(attachmentInfo.get(), messageData, sizeof(AttachmentInfo) * attachmentCount); 220 messageData += sizeof(AttachmentInfo) * attachmentCount; 221 222 for (size_t i = 0; i < attachmentCount; ++i) { 223 switch (attachmentInfo[i].getType()) { 224 case Attachment::MappedMemoryType: 225 case Attachment::SocketType: 226 if (!attachmentInfo[i].isNull()) 227 attachmentFileDescriptorCount++; 228 break; 229 case Attachment::Uninitialized: 230 default: 231 ASSERT_NOT_REACHED(); 232 break; 233 } 234 } 235 236 if (messageInfo.isMessageBodyIsOutOfLine()) 237 attachmentCount--; 238 } 239 240 Vector<Attachment> attachments(attachmentCount); 241 AttachmentResourceGuard<Vector<Attachment>, Vector<Attachment>::iterator> attachementDisposer(attachments); 242 RefPtr<WebKit::SharedMemory> oolMessageBody; 243 244 size_t fdIndex = 0; 245 for (size_t i = 0; i < attachmentCount; ++i) { 246 int fd = -1; 247 switch (attachmentInfo[i].getType()) { 248 case Attachment::MappedMemoryType: 249 if (!attachmentInfo[i].isNull()) 250 fd = m_fileDescriptors[fdIndex++]; 251 attachments[attachmentCount - i - 1] = Attachment(fd, attachmentInfo[i].getSize()); 252 break; 253 case Attachment::SocketType: 254 if (!attachmentInfo[i].isNull()) 255 fd = m_fileDescriptors[fdIndex++]; 256 attachments[attachmentCount - i - 1] = Attachment(fd); 257 break; 258 case Attachment::Uninitialized: 259 attachments[attachmentCount - i - 1] = Attachment(); 260 default: 261 break; 262 } 263 } 264 265 if (messageInfo.isMessageBodyIsOutOfLine()) { 266 ASSERT(messageInfo.bodySize()); 267 268 if (attachmentInfo[attachmentCount].isNull()) { 269 ASSERT_NOT_REACHED(); 270 return false; 271 } 272 273 WebKit::SharedMemory::Handle handle; 274 handle.adoptFromAttachment(m_fileDescriptors[attachmentFileDescriptorCount - 1], attachmentInfo[attachmentCount].getSize()); 275 276 oolMessageBody = WebKit::SharedMemory::create(handle, WebKit::SharedMemory::ReadOnly); 277 if (!oolMessageBody) { 278 ASSERT_NOT_REACHED(); 279 return false; 280 } 281 } 282 283 ASSERT(attachments.size() == (messageInfo.isMessageBodyIsOutOfLine() ? messageInfo.attachmentCount() - 1 : messageInfo.attachmentCount())); 284 285 uint8_t* messageBody = messageData; 286 if (messageInfo.isMessageBodyIsOutOfLine()) 287 messageBody = reinterpret_cast<uint8_t*>(oolMessageBody->data()); 288 289 OwnPtr<MessageDecoder> decoder; 290 if (attachments.isEmpty()) 291 decoder = MessageDecoder::create(DataReference(messageBody, messageInfo.bodySize())); 292 else 293 decoder = MessageDecoder::create(DataReference(messageBody, messageInfo.bodySize()), attachments); 294 295 processIncomingMessage(decoder.release()); 296 297 if (m_readBufferSize > messageLength) { 298 memmove(m_readBuffer.data(), m_readBuffer.data() + messageLength, m_readBufferSize - messageLength); 299 m_readBufferSize -= messageLength; 300 } else 301 m_readBufferSize = 0; 302 303 if (attachmentFileDescriptorCount) { 304 if (m_fileDescriptorsSize > attachmentFileDescriptorCount) { 305 size_t fileDescriptorsLength = attachmentFileDescriptorCount * sizeof(int); 306 memmove(m_fileDescriptors.data(), m_fileDescriptors.data() + fileDescriptorsLength, m_fileDescriptorsSize - fileDescriptorsLength); 307 m_fileDescriptorsSize -= fileDescriptorsLength; 308 } else 309 m_fileDescriptorsSize = 0; 310 } 311 312 313 return true; 314} 315 316static ssize_t readBytesFromSocket(int socketDescriptor, uint8_t* buffer, int count, int* fileDescriptors, size_t* fileDescriptorsCount) 317{ 318 struct msghdr message; 319 memset(&message, 0, sizeof(message)); 320 321 struct iovec iov[1]; 322 memset(&iov, 0, sizeof(iov)); 323 324 message.msg_controllen = CMSG_SPACE(sizeof(int) * attachmentMaxAmount); 325 OwnArrayPtr<char> attachmentDescriptorBuffer = adoptArrayPtr(new char[message.msg_controllen]); 326 memset(attachmentDescriptorBuffer.get(), 0, message.msg_controllen); 327 message.msg_control = attachmentDescriptorBuffer.get(); 328 329 iov[0].iov_base = buffer; 330 iov[0].iov_len = count; 331 332 message.msg_iov = iov; 333 message.msg_iovlen = 1; 334 335 while (true) { 336 ssize_t bytesRead = recvmsg(socketDescriptor, &message, 0); 337 338 if (bytesRead < 0) { 339 if (errno == EINTR) 340 continue; 341 342 return -1; 343 } 344 345 bool found = false; 346 struct cmsghdr* controlMessage; 347 for (controlMessage = CMSG_FIRSTHDR(&message); controlMessage; controlMessage = CMSG_NXTHDR(&message, controlMessage)) { 348 if (controlMessage->cmsg_level == SOL_SOCKET && controlMessage->cmsg_type == SCM_RIGHTS) { 349 *fileDescriptorsCount = (controlMessage->cmsg_len - CMSG_LEN(0)) / sizeof(int); 350 memcpy(fileDescriptors, CMSG_DATA(controlMessage), sizeof(int) * *fileDescriptorsCount); 351 352 for (size_t i = 0; i < *fileDescriptorsCount; ++i) { 353 while (fcntl(fileDescriptors[i], F_SETFL, FD_CLOEXEC) == -1) { 354 if (errno != EINTR) { 355 ASSERT_NOT_REACHED(); 356 break; 357 } 358 } 359 } 360 361 found = true; 362 break; 363 } 364 } 365 366 if (!found) 367 *fileDescriptorsCount = 0; 368 369 return bytesRead; 370 } 371 372 return -1; 373} 374 375void Connection::readyReadHandler() 376{ 377#if PLATFORM(QT) 378 SocketNotifierResourceGuard socketNotifierEnabler(m_socketNotifier); 379#endif 380 381 while (true) { 382 size_t fileDescriptorsCount = 0; 383 size_t bytesToRead = m_readBuffer.size() - m_readBufferSize; 384 ssize_t bytesRead = readBytesFromSocket(m_socketDescriptor, m_readBuffer.data() + m_readBufferSize, bytesToRead, 385 m_fileDescriptors.data() + m_fileDescriptorsSize, &fileDescriptorsCount); 386 387 if (bytesRead < 0) { 388 // EINTR was already handled by readBytesFromSocket. 389 if (errno == EAGAIN || errno == EWOULDBLOCK) 390 return; 391 392 // FIXME: Handle other errors here? 393 return; 394 } 395 396 m_readBufferSize += bytesRead; 397 m_fileDescriptorsSize += fileDescriptorsCount; 398 399 if (!bytesRead) { 400 connectionDidClose(); 401 return; 402 } 403 404 // Process messages from data received. 405 while (true) { 406 if (!processMessage()) 407 break; 408 } 409 } 410} 411 412bool Connection::open() 413{ 414#if PLATFORM(QT) 415 ASSERT(!m_socketNotifier); 416#endif 417 418 int flags = fcntl(m_socketDescriptor, F_GETFL, 0); 419 while (fcntl(m_socketDescriptor, F_SETFL, flags | O_NONBLOCK) == -1) { 420 if (errno != EINTR) { 421 ASSERT_NOT_REACHED(); 422 return false; 423 } 424 } 425 426 m_isConnected = true; 427#if PLATFORM(QT) 428 m_socketNotifier = m_connectionQueue->registerSocketEventHandler(m_socketDescriptor, QSocketNotifier::Read, WTF::bind(&Connection::readyReadHandler, this)); 429#elif PLATFORM(GTK) 430 m_connectionQueue->registerSocketEventHandler(m_socketDescriptor, G_IO_IN, WTF::bind(&Connection::readyReadHandler, this), WTF::bind(&Connection::connectionDidClose, this)); 431#elif PLATFORM(EFL) 432 m_connectionQueue->registerSocketEventHandler(m_socketDescriptor, WTF::bind(&Connection::readyReadHandler, this)); 433#endif 434 435 // Schedule a call to readyReadHandler. Data may have arrived before installation of the signal 436 // handler. 437 m_connectionQueue->dispatch(WTF::bind(&Connection::readyReadHandler, this)); 438 439 return true; 440} 441 442bool Connection::platformCanSendOutgoingMessages() const 443{ 444 return m_isConnected; 445} 446 447bool Connection::sendOutgoingMessage(PassOwnPtr<MessageEncoder> encoder) 448{ 449#if PLATFORM(QT) 450 ASSERT(m_socketNotifier); 451#endif 452 453 COMPILE_ASSERT(sizeof(MessageInfo) + attachmentMaxAmount * sizeof(size_t) <= messageMaxSize, AttachmentsFitToMessageInline); 454 455 Vector<Attachment> attachments = encoder->releaseAttachments(); 456 AttachmentResourceGuard<Vector<Attachment>, Vector<Attachment>::iterator> attachementDisposer(attachments); 457 458 if (attachments.size() > (attachmentMaxAmount - 1)) { 459 ASSERT_NOT_REACHED(); 460 return false; 461 } 462 463 MessageInfo messageInfo(encoder->bufferSize(), attachments.size()); 464 size_t messageSizeWithBodyInline = sizeof(messageInfo) + (attachments.size() * sizeof(AttachmentInfo)) + encoder->bufferSize(); 465 if (messageSizeWithBodyInline > messageMaxSize && encoder->bufferSize()) { 466 RefPtr<WebKit::SharedMemory> oolMessageBody = WebKit::SharedMemory::create(encoder->bufferSize()); 467 if (!oolMessageBody) 468 return false; 469 470 WebKit::SharedMemory::Handle handle; 471 if (!oolMessageBody->createHandle(handle, WebKit::SharedMemory::ReadOnly)) 472 return false; 473 474 messageInfo.setMessageBodyIsOutOfLine(); 475 476 memcpy(oolMessageBody->data(), encoder->buffer(), encoder->bufferSize()); 477 478 attachments.append(handle.releaseToAttachment()); 479 } 480 481 struct msghdr message; 482 memset(&message, 0, sizeof(message)); 483 484 struct iovec iov[3]; 485 memset(&iov, 0, sizeof(iov)); 486 487 message.msg_iov = iov; 488 int iovLength = 1; 489 490 iov[0].iov_base = reinterpret_cast<void*>(&messageInfo); 491 iov[0].iov_len = sizeof(messageInfo); 492 493 OwnArrayPtr<AttachmentInfo> attachmentInfo = adoptArrayPtr(new AttachmentInfo[attachments.size()]); 494 495 size_t attachmentFDBufferLength = 0; 496 if (!attachments.isEmpty()) { 497 for (size_t i = 0; i < attachments.size(); ++i) { 498 if (attachments[i].fileDescriptor() != -1) 499 attachmentFDBufferLength++; 500 } 501 } 502 OwnArrayPtr<char> attachmentFDBuffer = adoptArrayPtr(new char[CMSG_SPACE(sizeof(int) * attachmentFDBufferLength)]); 503 504 if (!attachments.isEmpty()) { 505 int* fdPtr = 0; 506 507 if (attachmentFDBufferLength) { 508 message.msg_control = attachmentFDBuffer.get(); 509 message.msg_controllen = CMSG_SPACE(sizeof(int) * attachmentFDBufferLength); 510 memset(message.msg_control, 0, message.msg_controllen); 511 512 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&message); 513 cmsg->cmsg_level = SOL_SOCKET; 514 cmsg->cmsg_type = SCM_RIGHTS; 515 cmsg->cmsg_len = CMSG_LEN(sizeof(int) * attachmentFDBufferLength); 516 517 fdPtr = reinterpret_cast<int*>(CMSG_DATA(cmsg)); 518 } 519 520 int fdIndex = 0; 521 for (size_t i = 0; i < attachments.size(); ++i) { 522 attachmentInfo[i].setType(attachments[i].type()); 523 524 switch (attachments[i].type()) { 525 case Attachment::MappedMemoryType: 526 attachmentInfo[i].setSize(attachments[i].size()); 527 // Fall trhough, set file descriptor or null. 528 case Attachment::SocketType: 529 if (attachments[i].fileDescriptor() != -1) { 530 ASSERT(fdPtr); 531 fdPtr[fdIndex++] = attachments[i].fileDescriptor(); 532 } else 533 attachmentInfo[i].setNull(); 534 break; 535 case Attachment::Uninitialized: 536 default: 537 break; 538 } 539 } 540 541 iov[iovLength].iov_base = attachmentInfo.get(); 542 iov[iovLength].iov_len = sizeof(AttachmentInfo) * attachments.size(); 543 ++iovLength; 544 } 545 546 if (!messageInfo.isMessageBodyIsOutOfLine() && encoder->bufferSize()) { 547 iov[iovLength].iov_base = reinterpret_cast<void*>(encoder->buffer()); 548 iov[iovLength].iov_len = encoder->bufferSize(); 549 ++iovLength; 550 } 551 552 message.msg_iovlen = iovLength; 553 554 int bytesSent = 0; 555 while ((bytesSent = sendmsg(m_socketDescriptor, &message, 0)) == -1) { 556 if (errno != EINTR) 557 return false; 558 } 559 return true; 560} 561 562#if PLATFORM(QT) 563void Connection::setShouldCloseConnectionOnProcessTermination(WebKit::PlatformProcessIdentifier process) 564{ 565 m_connectionQueue->dispatchOnTermination(process, WTF::bind(&Connection::connectionDidClose, this)); 566} 567#endif 568 569} // namespace CoreIPC 570