Add WTF::move()
[WebKit-https.git] / Source / WebKit2 / Platform / IPC / unix / ConnectionUnix.cpp
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 <poll.h>
38 #include <wtf/Assertions.h>
39 #include <wtf/Functional.h>
40 #include <wtf/StdLibExtras.h>
41 #include <wtf/UniStdExtras.h>
42
43 #if PLATFORM(GTK)
44 #include <glib.h>
45 #endif
46
47 #ifdef SOCK_SEQPACKET
48 #define SOCKET_TYPE SOCK_SEQPACKET
49 #else
50 #if PLATFORM(GTK)
51 #define SOCKET_TYPE SOCK_STREAM
52 #else
53 #define SOCKET_TYPE SOCK_DGRAM
54 #endif
55 #endif // SOCK_SEQPACKET
56
57 namespace IPC {
58
59 static const size_t messageMaxSize = 4096;
60 static const size_t attachmentMaxAmount = 255;
61
62 enum {
63     MessageBodyIsOutOfLine = 1U << 31
64 };
65
66 class MessageInfo {
67 public:
68     MessageInfo() { }
69
70     MessageInfo(size_t bodySize, size_t initialAttachmentCount)
71         : m_bodySize(bodySize)
72         , m_attachmentCount(initialAttachmentCount)
73         , m_isMessageBodyOutOfLine(false)
74     {
75     }
76
77     void setMessageBodyIsOutOfLine()
78     {
79         ASSERT(!isMessageBodyIsOutOfLine());
80
81         m_isMessageBodyOutOfLine = true;
82         m_attachmentCount++;
83     }
84
85     bool isMessageBodyIsOutOfLine() const { return m_isMessageBodyOutOfLine; }
86
87     size_t bodySize() const { return m_bodySize; }
88
89     size_t attachmentCount() const { return m_attachmentCount; }
90
91 private:
92     size_t m_bodySize;
93     size_t m_attachmentCount;
94     bool m_isMessageBodyOutOfLine;
95 };
96
97 class AttachmentInfo {
98 public:
99     AttachmentInfo()
100         : m_type(Attachment::Uninitialized)
101         , m_size(0)
102         , m_isNull(false)
103     {
104     }
105
106     void setType(Attachment::Type type) { m_type = type; }
107     Attachment::Type getType() { return m_type; }
108     void setSize(size_t size)
109     {
110         ASSERT(m_type == Attachment::MappedMemoryType);
111         m_size = size;
112     }
113
114     size_t getSize()
115     {
116         ASSERT(m_type == Attachment::MappedMemoryType);
117         return m_size;
118     }
119
120     // The attachment is not null unless explicitly set.
121     void setNull() { m_isNull = true; }
122     bool isNull() { return m_isNull; }
123
124 private:
125     Attachment::Type m_type;
126     size_t m_size;
127     bool m_isNull;
128 };
129
130 void Connection::platformInitialize(Identifier identifier)
131 {
132     m_socketDescriptor = identifier;
133     m_readBuffer.resize(messageMaxSize);
134     m_readBufferSize = 0;
135     m_fileDescriptors.resize(attachmentMaxAmount);
136     m_fileDescriptorsSize = 0;
137 }
138
139 void Connection::platformInvalidate()
140 {
141     // In GTK+ platform the socket is closed by the work queue.
142 #if !PLATFORM(GTK)
143     if (m_socketDescriptor != -1)
144         closeWithRetry(m_socketDescriptor);
145 #endif
146
147     if (!m_isConnected)
148         return;
149
150 #if PLATFORM(GTK) || PLATFORM(EFL)
151     m_connectionQueue->unregisterSocketEventHandler(m_socketDescriptor);
152 #endif
153
154     m_socketDescriptor = -1;
155     m_isConnected = false;
156 }
157
158 template<class T, class iterator>
159 class AttachmentResourceGuard {
160 public:
161     AttachmentResourceGuard(T& attachments)
162         : m_attachments(attachments)
163     {
164     }
165     ~AttachmentResourceGuard()
166     {
167         iterator end = m_attachments.end();
168         for (iterator i = m_attachments.begin(); i != end; ++i)
169             i->dispose();
170     }
171 private:
172     T& m_attachments;
173 };
174
175 bool Connection::processMessage()
176 {
177     if (m_readBufferSize < sizeof(MessageInfo))
178         return false;
179
180     uint8_t* messageData = m_readBuffer.data();
181     MessageInfo messageInfo;
182     memcpy(&messageInfo, messageData, sizeof(messageInfo));
183     messageData += sizeof(messageInfo);
184
185     size_t messageLength = sizeof(MessageInfo) + messageInfo.attachmentCount() * sizeof(AttachmentInfo) + (messageInfo.isMessageBodyIsOutOfLine() ? 0 : messageInfo.bodySize());
186     if (m_readBufferSize < messageLength)
187         return false;
188
189     size_t attachmentFileDescriptorCount = 0;
190     size_t attachmentCount = messageInfo.attachmentCount();
191     std::unique_ptr<AttachmentInfo[]> attachmentInfo;
192
193     if (attachmentCount) {
194         attachmentInfo = std::make_unique<AttachmentInfo[]>(attachmentCount);
195         memcpy(attachmentInfo.get(), messageData, sizeof(AttachmentInfo) * attachmentCount);
196         messageData += sizeof(AttachmentInfo) * attachmentCount;
197
198         for (size_t i = 0; i < attachmentCount; ++i) {
199             switch (attachmentInfo[i].getType()) {
200             case Attachment::MappedMemoryType:
201             case Attachment::SocketType:
202                 if (!attachmentInfo[i].isNull())
203                     attachmentFileDescriptorCount++;
204                 break;
205             case Attachment::Uninitialized:
206             default:
207                 ASSERT_NOT_REACHED();
208                 break;
209             }
210         }
211
212         if (messageInfo.isMessageBodyIsOutOfLine())
213             attachmentCount--;
214     }
215
216     Vector<Attachment> attachments(attachmentCount);
217     AttachmentResourceGuard<Vector<Attachment>, Vector<Attachment>::iterator> attachementDisposer(attachments);
218     RefPtr<WebKit::SharedMemory> oolMessageBody;
219
220     size_t fdIndex = 0;
221     for (size_t i = 0; i < attachmentCount; ++i) {
222         int fd = -1;
223         switch (attachmentInfo[i].getType()) {
224         case Attachment::MappedMemoryType:
225             if (!attachmentInfo[i].isNull())
226                 fd = m_fileDescriptors[fdIndex++];
227             attachments[attachmentCount - i - 1] = Attachment(fd, attachmentInfo[i].getSize());
228             break;
229         case Attachment::SocketType:
230             if (!attachmentInfo[i].isNull())
231                 fd = m_fileDescriptors[fdIndex++];
232             attachments[attachmentCount - i - 1] = Attachment(fd);
233             break;
234         case Attachment::Uninitialized:
235             attachments[attachmentCount - i - 1] = Attachment();
236         default:
237             break;
238         }
239     }
240
241     if (messageInfo.isMessageBodyIsOutOfLine()) {
242         ASSERT(messageInfo.bodySize());
243
244         if (attachmentInfo[attachmentCount].isNull()) {
245             ASSERT_NOT_REACHED();
246             return false;
247         }
248
249         WebKit::SharedMemory::Handle handle;
250         handle.adoptFromAttachment(m_fileDescriptors[attachmentFileDescriptorCount - 1], attachmentInfo[attachmentCount].getSize());
251
252         oolMessageBody = WebKit::SharedMemory::create(handle, WebKit::SharedMemory::ReadOnly);
253         if (!oolMessageBody) {
254             ASSERT_NOT_REACHED();
255             return false;
256         }
257     }
258
259     ASSERT(attachments.size() == (messageInfo.isMessageBodyIsOutOfLine() ? messageInfo.attachmentCount() - 1 : messageInfo.attachmentCount()));
260
261     uint8_t* messageBody = messageData;
262     if (messageInfo.isMessageBodyIsOutOfLine())
263         messageBody = reinterpret_cast<uint8_t*>(oolMessageBody->data());
264
265     auto decoder = std::make_unique<MessageDecoder>(DataReference(messageBody, messageInfo.bodySize()), WTF::move(attachments));
266
267     processIncomingMessage(WTF::move(decoder));
268
269     if (m_readBufferSize > messageLength) {
270         memmove(m_readBuffer.data(), m_readBuffer.data() + messageLength, m_readBufferSize - messageLength);
271         m_readBufferSize -= messageLength;
272     } else
273         m_readBufferSize = 0;
274
275     if (attachmentFileDescriptorCount) {
276         if (m_fileDescriptorsSize > attachmentFileDescriptorCount) {
277             size_t fileDescriptorsLength = attachmentFileDescriptorCount * sizeof(int);
278             memmove(m_fileDescriptors.data(), m_fileDescriptors.data() + fileDescriptorsLength, m_fileDescriptorsSize - fileDescriptorsLength);
279             m_fileDescriptorsSize -= fileDescriptorsLength;
280         } else
281             m_fileDescriptorsSize = 0;
282     }
283
284
285     return true;
286 }
287
288 static ssize_t readBytesFromSocket(int socketDescriptor, uint8_t* buffer, int count, int* fileDescriptors, size_t* fileDescriptorsCount)
289 {
290     struct msghdr message;
291     memset(&message, 0, sizeof(message));
292
293     struct iovec iov[1];
294     memset(&iov, 0, sizeof(iov));
295
296     message.msg_controllen = CMSG_SPACE(sizeof(int) * attachmentMaxAmount);
297     auto attachmentDescriptorBuffer = std::make_unique<char[]>(message.msg_controllen);
298     memset(attachmentDescriptorBuffer.get(), 0, message.msg_controllen);
299     message.msg_control = attachmentDescriptorBuffer.get();
300
301     iov[0].iov_base = buffer;
302     iov[0].iov_len = count;
303
304     message.msg_iov = iov;
305     message.msg_iovlen = 1;
306
307     while (true) {
308         ssize_t bytesRead = recvmsg(socketDescriptor, &message, 0);
309
310         if (bytesRead < 0) {
311             if (errno == EINTR)
312                 continue;
313
314             return -1;
315         }
316
317         bool found = false;
318         struct cmsghdr* controlMessage;
319         for (controlMessage = CMSG_FIRSTHDR(&message); controlMessage; controlMessage = CMSG_NXTHDR(&message, controlMessage)) {
320             if (controlMessage->cmsg_level == SOL_SOCKET && controlMessage->cmsg_type == SCM_RIGHTS) {
321                 *fileDescriptorsCount = (controlMessage->cmsg_len - CMSG_LEN(0)) / sizeof(int);
322                 memcpy(fileDescriptors, CMSG_DATA(controlMessage), sizeof(int) * *fileDescriptorsCount);
323
324                 for (size_t i = 0; i < *fileDescriptorsCount; ++i) {
325                     while (fcntl(fileDescriptors[i], F_SETFD, FD_CLOEXEC) == -1) {
326                         if (errno != EINTR) {
327                             ASSERT_NOT_REACHED();
328                             break;
329                         }
330                     }
331                 }
332
333                 found = true;
334                 break;
335             }
336         }
337
338         if (!found)
339             *fileDescriptorsCount = 0;
340
341         return bytesRead;
342     }
343
344     return -1;
345 }
346
347 void Connection::readyReadHandler()
348 {
349     while (true) {
350         size_t fileDescriptorsCount = 0;
351         size_t bytesToRead = m_readBuffer.size() - m_readBufferSize;
352         ssize_t bytesRead = readBytesFromSocket(m_socketDescriptor, m_readBuffer.data() + m_readBufferSize, bytesToRead,
353                                                 m_fileDescriptors.data() + m_fileDescriptorsSize, &fileDescriptorsCount);
354
355         if (bytesRead < 0) {
356             // EINTR was already handled by readBytesFromSocket.
357             if (errno == EAGAIN || errno == EWOULDBLOCK)
358                 return;
359
360             WTFLogAlways("Error receiving IPC message on socket %d in process %d: %s", m_socketDescriptor, getpid(), strerror(errno));
361             connectionDidClose();
362             return;
363         }
364
365         m_readBufferSize += bytesRead;
366         m_fileDescriptorsSize += fileDescriptorsCount;
367
368         if (!bytesRead) {
369             connectionDidClose();
370             return;
371         }
372
373         // Process messages from data received.
374         while (true) {
375             if (!processMessage())
376                 break;
377         }
378     }
379 }
380
381 bool Connection::open()
382 {
383     int flags = fcntl(m_socketDescriptor, F_GETFL, 0);
384     while (fcntl(m_socketDescriptor, F_SETFL, flags | O_NONBLOCK) == -1) {
385         if (errno != EINTR) {
386             ASSERT_NOT_REACHED();
387             return false;
388         }
389     }
390
391     m_isConnected = true;
392 #if PLATFORM(GTK)
393     RefPtr<Connection> protector(this);
394     m_connectionQueue->registerSocketEventHandler(m_socketDescriptor,
395         [=] {
396             protector->readyReadHandler();
397         },
398         [=] {
399             protector->connectionDidClose();
400         });
401 #elif PLATFORM(EFL)
402     RefPtr<Connection> protector(this);
403     m_connectionQueue->registerSocketEventHandler(m_socketDescriptor,
404         [protector] {
405             protector->readyReadHandler();
406         });
407 #endif
408
409     // Schedule a call to readyReadHandler. Data may have arrived before installation of the signal
410     // handler.
411     m_connectionQueue->dispatch(WTF::bind(&Connection::readyReadHandler, this));
412
413     return true;
414 }
415
416 bool Connection::platformCanSendOutgoingMessages() const
417 {
418     return m_isConnected;
419 }
420
421 bool Connection::sendOutgoingMessage(std::unique_ptr<MessageEncoder> encoder)
422 {
423     COMPILE_ASSERT(sizeof(MessageInfo) + attachmentMaxAmount * sizeof(size_t) <= messageMaxSize, AttachmentsFitToMessageInline);
424
425     Vector<Attachment> attachments = encoder->releaseAttachments();
426     AttachmentResourceGuard<Vector<Attachment>, Vector<Attachment>::iterator> attachementDisposer(attachments);
427
428     if (attachments.size() > (attachmentMaxAmount - 1)) {
429         ASSERT_NOT_REACHED();
430         return false;
431     }
432
433     MessageInfo messageInfo(encoder->bufferSize(), attachments.size());
434     size_t messageSizeWithBodyInline = sizeof(messageInfo) + (attachments.size() * sizeof(AttachmentInfo)) + encoder->bufferSize();
435     if (messageSizeWithBodyInline > messageMaxSize && encoder->bufferSize()) {
436         RefPtr<WebKit::SharedMemory> oolMessageBody = WebKit::SharedMemory::create(encoder->bufferSize());
437         if (!oolMessageBody)
438             return false;
439
440         WebKit::SharedMemory::Handle handle;
441         if (!oolMessageBody->createHandle(handle, WebKit::SharedMemory::ReadOnly))
442             return false;
443
444         messageInfo.setMessageBodyIsOutOfLine();
445
446         memcpy(oolMessageBody->data(), encoder->buffer(), encoder->bufferSize());
447
448         attachments.append(handle.releaseToAttachment());
449     }
450
451     struct msghdr message;
452     memset(&message, 0, sizeof(message));
453
454     struct iovec iov[3];
455     memset(&iov, 0, sizeof(iov));
456
457     message.msg_iov = iov;
458     int iovLength = 1;
459
460     iov[0].iov_base = reinterpret_cast<void*>(&messageInfo);
461     iov[0].iov_len = sizeof(messageInfo);
462
463     auto attachmentInfo = std::make_unique<AttachmentInfo[]>(attachments.size());
464
465     size_t attachmentFDBufferLength = 0;
466     if (!attachments.isEmpty()) {
467         for (size_t i = 0; i < attachments.size(); ++i) {
468             if (attachments[i].fileDescriptor() != -1)
469                 attachmentFDBufferLength++;
470         }
471     }
472     auto attachmentFDBuffer = std::make_unique<char[]>(CMSG_SPACE(sizeof(int) * attachmentFDBufferLength));
473
474     if (!attachments.isEmpty()) {
475         int* fdPtr = 0;
476
477         if (attachmentFDBufferLength) {
478             message.msg_control = attachmentFDBuffer.get();
479             message.msg_controllen = CMSG_SPACE(sizeof(int) * attachmentFDBufferLength);
480             memset(message.msg_control, 0, message.msg_controllen);
481
482             struct cmsghdr* cmsg = CMSG_FIRSTHDR(&message);
483             cmsg->cmsg_level = SOL_SOCKET;
484             cmsg->cmsg_type = SCM_RIGHTS;
485             cmsg->cmsg_len = CMSG_LEN(sizeof(int) * attachmentFDBufferLength);
486
487             fdPtr = reinterpret_cast<int*>(CMSG_DATA(cmsg));
488         }
489
490         int fdIndex = 0;
491         for (size_t i = 0; i < attachments.size(); ++i) {
492             attachmentInfo[i].setType(attachments[i].type());
493
494             switch (attachments[i].type()) {
495             case Attachment::MappedMemoryType:
496                 attachmentInfo[i].setSize(attachments[i].size());
497                 // Fall trhough, set file descriptor or null.
498             case Attachment::SocketType:
499                 if (attachments[i].fileDescriptor() != -1) {
500                     ASSERT(fdPtr);
501                     fdPtr[fdIndex++] = attachments[i].fileDescriptor();
502                 } else
503                     attachmentInfo[i].setNull();
504                 break;
505             case Attachment::Uninitialized:
506             default:
507                 break;
508             }
509         }
510
511         iov[iovLength].iov_base = attachmentInfo.get();
512         iov[iovLength].iov_len = sizeof(AttachmentInfo) * attachments.size();
513         ++iovLength;
514     }
515
516     if (!messageInfo.isMessageBodyIsOutOfLine() && encoder->bufferSize()) {
517         iov[iovLength].iov_base = reinterpret_cast<void*>(encoder->buffer());
518         iov[iovLength].iov_len = encoder->bufferSize();
519         ++iovLength;
520     }
521
522     message.msg_iovlen = iovLength;
523
524     int bytesSent = 0;
525     while ((bytesSent = sendmsg(m_socketDescriptor, &message, 0)) == -1) {
526         if (errno == EINTR)
527             continue;
528         if (errno == EAGAIN || errno == EWOULDBLOCK) {
529             struct pollfd pollfd;
530
531             pollfd.fd = m_socketDescriptor;
532             pollfd.events = POLLOUT;
533             pollfd.revents = 0;
534             poll(&pollfd, 1, -1);
535             continue;
536         }
537
538         WTFLogAlways("Error sending IPC message: %s", strerror(errno));
539         return false;
540     }
541     return true;
542 }
543
544 Connection::SocketPair Connection::createPlatformConnection(unsigned options)
545 {
546     int sockets[2];
547     RELEASE_ASSERT(socketpair(AF_UNIX, SOCKET_TYPE, 0, sockets) != -1);
548
549     if (options & SetCloexecOnServer) {
550         // Don't expose the child socket to the parent process.
551         while (fcntl(sockets[1], F_SETFD, FD_CLOEXEC)  == -1)
552             RELEASE_ASSERT(errno != EINTR);
553     }
554
555     if (options & SetCloexecOnClient) {
556         // Don't expose the parent socket to potential future children.
557         while (fcntl(sockets[0], F_SETFD, FD_CLOEXEC) == -1)
558             RELEASE_ASSERT(errno != EINTR);
559     }
560
561     SocketPair socketPair = { sockets[0], sockets[1] };
562     return socketPair;
563 }
564     
565 void Connection::willSendSyncMessage(unsigned flags)
566 {
567     UNUSED_PARAM(flags);
568 }
569     
570 void Connection::didReceiveSyncReply(unsigned flags)
571 {
572     UNUSED_PARAM(flags);    
573 }
574
575 } // namespace IPC