OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ipc/ipc_channel_posix.h" | 5 #include "ipc/ipc_channel_posix.h" |
6 | 6 |
7 #include <errno.h> | 7 #include <errno.h> |
8 #include <fcntl.h> | 8 #include <fcntl.h> |
9 #include <stddef.h> | 9 #include <stddef.h> |
10 #include <sys/socket.h> | 10 #include <sys/socket.h> |
(...skipping 22 matching lines...) Expand all Loading... |
33 #include "base/rand_util.h" | 33 #include "base/rand_util.h" |
34 #include "base/stl_util.h" | 34 #include "base/stl_util.h" |
35 #include "base/string_util.h" | 35 #include "base/string_util.h" |
36 #include "base/synchronization/lock.h" | 36 #include "base/synchronization/lock.h" |
37 #include "ipc/file_descriptor_set_posix.h" | 37 #include "ipc/file_descriptor_set_posix.h" |
38 #include "ipc/ipc_descriptors.h" | 38 #include "ipc/ipc_descriptors.h" |
39 #include "ipc/ipc_listener.h" | 39 #include "ipc/ipc_listener.h" |
40 #include "ipc/ipc_logging.h" | 40 #include "ipc/ipc_logging.h" |
41 #include "ipc/ipc_message_utils.h" | 41 #include "ipc/ipc_message_utils.h" |
42 #include "ipc/ipc_switches.h" | 42 #include "ipc/ipc_switches.h" |
| 43 #include "ipc/unix_domain_socket_util.h" |
43 | 44 |
44 namespace IPC { | 45 namespace IPC { |
45 | 46 |
46 // IPC channels on Windows use named pipes (CreateNamedPipe()) with | 47 // IPC channels on Windows use named pipes (CreateNamedPipe()) with |
47 // channel ids as the pipe names. Channels on POSIX use sockets as | 48 // channel ids as the pipe names. Channels on POSIX use sockets as |
48 // pipes These don't quite line up. | 49 // pipes These don't quite line up. |
49 // | 50 // |
50 // When creating a child subprocess we use a socket pair and the parent side of | 51 // When creating a child subprocess we use a socket pair and the parent side of |
51 // the fork arranges it such that the initial control channel ends up on the | 52 // the fork arranges it such that the initial control channel ends up on the |
52 // magic file descriptor kPrimaryIPCChannel in the child. Future | 53 // magic file descriptor kPrimaryIPCChannel in the child. Future |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
130 | 131 |
131 private: | 132 private: |
132 base::Lock lock_; | 133 base::Lock lock_; |
133 typedef std::map<std::string, int> ChannelToFDMap; | 134 typedef std::map<std::string, int> ChannelToFDMap; |
134 ChannelToFDMap map_; | 135 ChannelToFDMap map_; |
135 | 136 |
136 friend struct DefaultSingletonTraits<PipeMap>; | 137 friend struct DefaultSingletonTraits<PipeMap>; |
137 }; | 138 }; |
138 | 139 |
139 //------------------------------------------------------------------------------ | 140 //------------------------------------------------------------------------------ |
140 // Verify that kMaxPipeNameLength is a decent size. | |
141 COMPILE_ASSERT(sizeof(((sockaddr_un*)0)->sun_path) >= kMaxPipeNameLength, | |
142 BAD_SUN_PATH_LENGTH); | |
143 | |
144 // Creates a unix domain socket bound to the specified name that is listening | |
145 // for connections. | |
146 bool CreateServerUnixDomainSocket(const std::string& pipe_name, | |
147 int* server_listen_fd) { | |
148 DCHECK(server_listen_fd); | |
149 | |
150 if (pipe_name.length() == 0 || pipe_name.length() >= kMaxPipeNameLength) { | |
151 DLOG(ERROR) << "pipe_name.length() == " << pipe_name.length(); | |
152 return false; | |
153 } | |
154 | |
155 // Create socket. | |
156 int fd = socket(AF_UNIX, SOCK_STREAM, 0); | |
157 if (fd < 0) { | |
158 return false; | |
159 } | |
160 | |
161 // Make socket non-blocking | |
162 if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { | |
163 PLOG(ERROR) << "fcntl(O_NONBLOCK) " << pipe_name; | |
164 if (HANDLE_EINTR(close(fd)) < 0) | |
165 PLOG(ERROR) << "close " << pipe_name; | |
166 return false; | |
167 } | |
168 | |
169 // Delete any old FS instances. | |
170 unlink(pipe_name.c_str()); | |
171 | |
172 // Make sure the path we need exists. | |
173 base::FilePath path(pipe_name); | |
174 base::FilePath dir_path = path.DirName(); | |
175 if (!file_util::CreateDirectory(dir_path)) { | |
176 if (HANDLE_EINTR(close(fd)) < 0) | |
177 PLOG(ERROR) << "close " << pipe_name; | |
178 return false; | |
179 } | |
180 | |
181 // Create unix_addr structure. | |
182 struct sockaddr_un unix_addr; | |
183 memset(&unix_addr, 0, sizeof(unix_addr)); | |
184 unix_addr.sun_family = AF_UNIX; | |
185 int path_len = snprintf(unix_addr.sun_path, IPC::kMaxPipeNameLength, | |
186 "%s", pipe_name.c_str()); | |
187 DCHECK_EQ(static_cast<int>(pipe_name.length()), path_len); | |
188 size_t unix_addr_len = offsetof(struct sockaddr_un, | |
189 sun_path) + path_len + 1; | |
190 | |
191 // Bind the socket. | |
192 if (bind(fd, reinterpret_cast<const sockaddr*>(&unix_addr), | |
193 unix_addr_len) != 0) { | |
194 PLOG(ERROR) << "bind " << pipe_name; | |
195 if (HANDLE_EINTR(close(fd)) < 0) | |
196 PLOG(ERROR) << "close " << pipe_name; | |
197 return false; | |
198 } | |
199 | |
200 // Start listening on the socket. | |
201 const int listen_queue_length = 1; | |
202 if (listen(fd, listen_queue_length) != 0) { | |
203 PLOG(ERROR) << "listen " << pipe_name; | |
204 if (HANDLE_EINTR(close(fd)) < 0) | |
205 PLOG(ERROR) << "close " << pipe_name; | |
206 return false; | |
207 } | |
208 | |
209 *server_listen_fd = fd; | |
210 return true; | |
211 } | |
212 | |
213 // Accept a connection on a socket we are listening to. | |
214 bool ServerAcceptConnection(int server_listen_fd, int* server_socket) { | |
215 DCHECK(server_socket); | |
216 | |
217 int accept_fd = HANDLE_EINTR(accept(server_listen_fd, NULL, 0)); | |
218 if (accept_fd < 0) | |
219 return false; | |
220 if (fcntl(accept_fd, F_SETFL, O_NONBLOCK) == -1) { | |
221 PLOG(ERROR) << "fcntl(O_NONBLOCK) " << accept_fd; | |
222 if (HANDLE_EINTR(close(accept_fd)) < 0) | |
223 PLOG(ERROR) << "close " << accept_fd; | |
224 return false; | |
225 } | |
226 | |
227 *server_socket = accept_fd; | |
228 return true; | |
229 } | |
230 | |
231 bool CreateClientUnixDomainSocket(const std::string& pipe_name, | |
232 int* client_socket) { | |
233 DCHECK(client_socket); | |
234 DCHECK_GT(pipe_name.length(), 0u); | |
235 DCHECK_LT(pipe_name.length(), kMaxPipeNameLength); | |
236 | |
237 if (pipe_name.length() == 0 || pipe_name.length() >= kMaxPipeNameLength) { | |
238 return false; | |
239 } | |
240 | |
241 // Create socket. | |
242 int fd = socket(AF_UNIX, SOCK_STREAM, 0); | |
243 if (fd < 0) { | |
244 PLOG(ERROR) << "socket " << pipe_name; | |
245 return false; | |
246 } | |
247 | |
248 // Make socket non-blocking | |
249 if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { | |
250 PLOG(ERROR) << "fcntl(O_NONBLOCK) " << pipe_name; | |
251 if (HANDLE_EINTR(close(fd)) < 0) | |
252 PLOG(ERROR) << "close " << pipe_name; | |
253 return false; | |
254 } | |
255 | |
256 // Create server side of socket. | |
257 struct sockaddr_un server_unix_addr; | |
258 memset(&server_unix_addr, 0, sizeof(server_unix_addr)); | |
259 server_unix_addr.sun_family = AF_UNIX; | |
260 int path_len = snprintf(server_unix_addr.sun_path, IPC::kMaxPipeNameLength, | |
261 "%s", pipe_name.c_str()); | |
262 DCHECK_EQ(static_cast<int>(pipe_name.length()), path_len); | |
263 size_t server_unix_addr_len = offsetof(struct sockaddr_un, | |
264 sun_path) + path_len + 1; | |
265 | |
266 if (HANDLE_EINTR(connect(fd, reinterpret_cast<sockaddr*>(&server_unix_addr), | |
267 server_unix_addr_len)) != 0) { | |
268 PLOG(ERROR) << "connect " << pipe_name; | |
269 if (HANDLE_EINTR(close(fd)) < 0) | |
270 PLOG(ERROR) << "close " << pipe_name; | |
271 return false; | |
272 } | |
273 | |
274 *client_socket = fd; | |
275 return true; | |
276 } | |
277 | 141 |
278 bool SocketWriteErrorIsRecoverable() { | 142 bool SocketWriteErrorIsRecoverable() { |
279 #if defined(OS_MACOSX) | 143 #if defined(OS_MACOSX) |
280 // On OS X if sendmsg() is trying to send fds between processes and there | 144 // On OS X if sendmsg() is trying to send fds between processes and there |
281 // isn't enough room in the output buffer to send the fd structure over | 145 // isn't enough room in the output buffer to send the fd structure over |
282 // atomically then EMSGSIZE is returned. | 146 // atomically then EMSGSIZE is returned. |
283 // | 147 // |
284 // EMSGSIZE presents a problem since the system APIs can only call us when | 148 // EMSGSIZE presents a problem since the system APIs can only call us when |
285 // there's room in the socket buffer and not when there is "enough" room. | 149 // there's room in the socket buffer and not when there is "enough" room. |
286 // | 150 // |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
381 return false; | 245 return false; |
382 } | 246 } |
383 if (!(value & O_NONBLOCK)) { | 247 if (!(value & O_NONBLOCK)) { |
384 LOG(ERROR) << "Socket " << pipe_name_ << " must be O_NONBLOCK"; | 248 LOG(ERROR) << "Socket " << pipe_name_ << " must be O_NONBLOCK"; |
385 return false; | 249 return false; |
386 } | 250 } |
387 #endif // IPC_USES_READWRITE | 251 #endif // IPC_USES_READWRITE |
388 } else if (mode_ & MODE_NAMED_FLAG) { | 252 } else if (mode_ & MODE_NAMED_FLAG) { |
389 // Case 2 from comment above. | 253 // Case 2 from comment above. |
390 if (mode_ & MODE_SERVER_FLAG) { | 254 if (mode_ & MODE_SERVER_FLAG) { |
391 if (!CreateServerUnixDomainSocket(pipe_name_, &local_pipe)) { | 255 if (!CreateServerUnixDomainSocket(base::FilePath(pipe_name_), |
| 256 &local_pipe)) { |
392 return false; | 257 return false; |
393 } | 258 } |
394 must_unlink_ = true; | 259 must_unlink_ = true; |
395 } else if (mode_ & MODE_CLIENT_FLAG) { | 260 } else if (mode_ & MODE_CLIENT_FLAG) { |
396 if (!CreateClientUnixDomainSocket(pipe_name_, &local_pipe)) { | 261 if (!CreateClientUnixDomainSocket(base::FilePath(pipe_name_), |
| 262 &local_pipe)) { |
397 return false; | 263 return false; |
398 } | 264 } |
399 } else { | 265 } else { |
400 LOG(ERROR) << "Bad mode: " << mode_; | 266 LOG(ERROR) << "Bad mode: " << mode_; |
401 return false; | 267 return false; |
402 } | 268 } |
403 } else { | 269 } else { |
404 local_pipe = PipeMap::GetInstance()->Lookup(pipe_name_); | 270 local_pipe = PipeMap::GetInstance()->Lookup(pipe_name_); |
405 if (mode_ & MODE_CLIENT_FLAG) { | 271 if (mode_ & MODE_CLIENT_FLAG) { |
406 if (local_pipe != -1) { | 272 if (local_pipe != -1) { |
(...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
669 bool Channel::ChannelImpl::AcceptsConnections() const { | 535 bool Channel::ChannelImpl::AcceptsConnections() const { |
670 return server_listen_pipe_ != -1; | 536 return server_listen_pipe_ != -1; |
671 } | 537 } |
672 | 538 |
673 bool Channel::ChannelImpl::HasAcceptedConnection() const { | 539 bool Channel::ChannelImpl::HasAcceptedConnection() const { |
674 return AcceptsConnections() && pipe_ != -1; | 540 return AcceptsConnections() && pipe_ != -1; |
675 } | 541 } |
676 | 542 |
677 bool Channel::ChannelImpl::GetClientEuid(uid_t* client_euid) const { | 543 bool Channel::ChannelImpl::GetClientEuid(uid_t* client_euid) const { |
678 DCHECK(HasAcceptedConnection()); | 544 DCHECK(HasAcceptedConnection()); |
679 #if defined(OS_MACOSX) || defined(OS_OPENBSD) | 545 return IPC::GetPeerEuid(pipe_, client_euid); |
680 uid_t peer_euid; | |
681 gid_t peer_gid; | |
682 if (getpeereid(pipe_, &peer_euid, &peer_gid) != 0) { | |
683 PLOG(ERROR) << "getpeereid " << pipe_; | |
684 return false; | |
685 } | |
686 *client_euid = peer_euid; | |
687 return true; | |
688 #elif defined(OS_SOLARIS) | |
689 return false; | |
690 #else | |
691 struct ucred cred; | |
692 socklen_t cred_len = sizeof(cred); | |
693 if (getsockopt(pipe_, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) != 0) { | |
694 PLOG(ERROR) << "getsockopt " << pipe_; | |
695 return false; | |
696 } | |
697 if (static_cast<unsigned>(cred_len) < sizeof(cred)) { | |
698 NOTREACHED() << "Truncated ucred from SO_PEERCRED?"; | |
699 return false; | |
700 } | |
701 *client_euid = cred.uid; | |
702 return true; | |
703 #endif | |
704 } | 546 } |
705 | 547 |
706 void Channel::ChannelImpl::ResetToAcceptingConnectionState() { | 548 void Channel::ChannelImpl::ResetToAcceptingConnectionState() { |
707 // Unregister libevent for the unix domain socket and close it. | 549 // Unregister libevent for the unix domain socket and close it. |
708 read_watcher_.StopWatchingFileDescriptor(); | 550 read_watcher_.StopWatchingFileDescriptor(); |
709 write_watcher_.StopWatchingFileDescriptor(); | 551 write_watcher_.StopWatchingFileDescriptor(); |
710 if (pipe_ != -1) { | 552 if (pipe_ != -1) { |
711 if (HANDLE_EINTR(close(pipe_)) < 0) | 553 if (HANDLE_EINTR(close(pipe_)) < 0) |
712 PLOG(ERROR) << "close pipe_ " << pipe_name_; | 554 PLOG(ERROR) << "close pipe_ " << pipe_name_; |
713 pipe_ = -1; | 555 pipe_ = -1; |
(...skipping 470 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1184 | 1026 |
1185 | 1027 |
1186 #if defined(OS_LINUX) | 1028 #if defined(OS_LINUX) |
1187 // static | 1029 // static |
1188 void Channel::SetGlobalPid(int pid) { | 1030 void Channel::SetGlobalPid(int pid) { |
1189 ChannelImpl::SetGlobalPid(pid); | 1031 ChannelImpl::SetGlobalPid(pid); |
1190 } | 1032 } |
1191 #endif // OS_LINUX | 1033 #endif // OS_LINUX |
1192 | 1034 |
1193 } // namespace IPC | 1035 } // namespace IPC |
OLD | NEW |