- add sources.
[platform/framework/web/crosswalk.git] / src / base / posix / unix_domain_socket_linux.cc
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/posix/unix_domain_socket_linux.h"
6
7 #include <errno.h>
8 #include <sys/socket.h>
9 #include <sys/uio.h>
10 #include <unistd.h>
11
12 #include "base/logging.h"
13 #include "base/pickle.h"
14 #include "base/posix/eintr_wrapper.h"
15 #include "base/stl_util.h"
16
17 const size_t UnixDomainSocket::kMaxFileDescriptors = 16;
18
19 // static
20 bool UnixDomainSocket::SendMsg(int fd,
21                                const void* buf,
22                                size_t length,
23                                const std::vector<int>& fds) {
24   struct msghdr msg = {};
25   struct iovec iov = { const_cast<void*>(buf), length };
26   msg.msg_iov = &iov;
27   msg.msg_iovlen = 1;
28
29   char* control_buffer = NULL;
30   if (fds.size()) {
31     const unsigned control_len = CMSG_SPACE(sizeof(int) * fds.size());
32     control_buffer = new char[control_len];
33
34     struct cmsghdr* cmsg;
35     msg.msg_control = control_buffer;
36     msg.msg_controllen = control_len;
37     cmsg = CMSG_FIRSTHDR(&msg);
38     cmsg->cmsg_level = SOL_SOCKET;
39     cmsg->cmsg_type = SCM_RIGHTS;
40     cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fds.size());
41     memcpy(CMSG_DATA(cmsg), &fds[0], sizeof(int) * fds.size());
42     msg.msg_controllen = cmsg->cmsg_len;
43   }
44
45   // Avoid a SIGPIPE if the other end breaks the connection.
46   // Due to a bug in the Linux kernel (net/unix/af_unix.c) MSG_NOSIGNAL isn't
47   // regarded for SOCK_SEQPACKET in the AF_UNIX domain, but it is mandated by
48   // POSIX.
49   const int flags = MSG_NOSIGNAL;
50   const ssize_t r = HANDLE_EINTR(sendmsg(fd, &msg, flags));
51   const bool ret = static_cast<ssize_t>(length) == r;
52   delete[] control_buffer;
53   return ret;
54 }
55
56 // static
57 ssize_t UnixDomainSocket::RecvMsg(int fd,
58                                   void* buf,
59                                   size_t length,
60                                   std::vector<int>* fds) {
61   return UnixDomainSocket::RecvMsgWithFlags(fd, buf, length, 0, fds);
62 }
63
64 // static
65 ssize_t UnixDomainSocket::RecvMsgWithFlags(int fd,
66                                            void* buf,
67                                            size_t length,
68                                            int flags,
69                                            std::vector<int>* fds) {
70   fds->clear();
71
72   struct msghdr msg = {};
73   struct iovec iov = { buf, length };
74   msg.msg_iov = &iov;
75   msg.msg_iovlen = 1;
76
77   char control_buffer[CMSG_SPACE(sizeof(int) * kMaxFileDescriptors)];
78   msg.msg_control = control_buffer;
79   msg.msg_controllen = sizeof(control_buffer);
80
81   const ssize_t r = HANDLE_EINTR(recvmsg(fd, &msg, flags));
82   if (r == -1)
83     return -1;
84
85   int* wire_fds = NULL;
86   unsigned wire_fds_len = 0;
87
88   if (msg.msg_controllen > 0) {
89     struct cmsghdr* cmsg;
90     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
91       if (cmsg->cmsg_level == SOL_SOCKET &&
92           cmsg->cmsg_type == SCM_RIGHTS) {
93         const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0);
94         DCHECK(payload_len % sizeof(int) == 0);
95         wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
96         wire_fds_len = payload_len / sizeof(int);
97         break;
98       }
99     }
100   }
101
102   if (msg.msg_flags & MSG_TRUNC || msg.msg_flags & MSG_CTRUNC) {
103     for (unsigned i = 0; i < wire_fds_len; ++i)
104       close(wire_fds[i]);
105     errno = EMSGSIZE;
106     return -1;
107   }
108
109   fds->resize(wire_fds_len);
110   memcpy(vector_as_array(fds), wire_fds, sizeof(int) * wire_fds_len);
111
112   return r;
113 }
114
115 // static
116 ssize_t UnixDomainSocket::SendRecvMsg(int fd,
117                                       uint8_t* reply,
118                                       unsigned max_reply_len,
119                                       int* result_fd,
120                                       const Pickle& request) {
121   return UnixDomainSocket::SendRecvMsgWithFlags(fd, reply, max_reply_len,
122                                                 0,  /* recvmsg_flags */
123                                                 result_fd, request);
124 }
125
126 // static
127 ssize_t UnixDomainSocket::SendRecvMsgWithFlags(int fd,
128                                                uint8_t* reply,
129                                                unsigned max_reply_len,
130                                                int recvmsg_flags,
131                                                int* result_fd,
132                                                const Pickle& request) {
133   int fds[2];
134
135   // This socketpair is only used for the IPC and is cleaned up before
136   // returning.
137   if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) == -1)
138     return -1;
139
140   std::vector<int> fd_vector;
141   fd_vector.push_back(fds[1]);
142   if (!SendMsg(fd, request.data(), request.size(), fd_vector)) {
143     close(fds[0]);
144     close(fds[1]);
145     return -1;
146   }
147   close(fds[1]);
148
149   fd_vector.clear();
150   // When porting to OSX keep in mind it doesn't support MSG_NOSIGNAL, so the
151   // sender might get a SIGPIPE.
152   const ssize_t reply_len = RecvMsgWithFlags(fds[0], reply, max_reply_len,
153                                              recvmsg_flags, &fd_vector);
154   close(fds[0]);
155   if (reply_len == -1)
156     return -1;
157
158   if ((!fd_vector.empty() && result_fd == NULL) || fd_vector.size() > 1) {
159     for (std::vector<int>::const_iterator
160          i = fd_vector.begin(); i != fd_vector.end(); ++i) {
161       close(*i);
162     }
163
164     NOTREACHED();
165
166     return -1;
167   }
168
169   if (result_fd)
170     *result_fd = fd_vector.empty() ? -1 : fd_vector[0];
171
172   return reply_len;
173 }