OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 "mojo/edk/embedder/platform_channel_pair.h" | 5 #include "mojo/edk/embedder/platform_channel_pair.h" |
6 | 6 |
7 #include <fcntl.h> | 7 #include <fcntl.h> |
8 #include <sys/socket.h> | 8 #include <sys/socket.h> |
9 #include <sys/types.h> | 9 #include <sys/types.h> |
10 #include <unistd.h> | 10 #include <unistd.h> |
(...skipping 20 matching lines...) Expand all Loading... |
31 int target_fd) { | 31 int target_fd) { |
32 for (size_t i = 0; i < file_handle_mapping.size(); i++) { | 32 for (size_t i = 0; i < file_handle_mapping.size(); i++) { |
33 if (file_handle_mapping[i].second == target_fd) | 33 if (file_handle_mapping[i].second == target_fd) |
34 return true; | 34 return true; |
35 } | 35 } |
36 return false; | 36 return false; |
37 } | 37 } |
38 | 38 |
39 } // namespace | 39 } // namespace |
40 | 40 |
41 PlatformChannelPair::PlatformChannelPair() { | 41 PlatformChannelPair::PlatformChannelPair(bool client_is_blocking) { |
42 // Create the Unix domain socket and set the ends to nonblocking. | 42 // Create the Unix domain socket. |
43 int fds[2]; | 43 int fds[2]; |
44 // TODO(vtl): Maybe fail gracefully if |socketpair()| fails. | 44 // TODO(vtl): Maybe fail gracefully if |socketpair()| fails. |
45 | 45 |
46 PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0); | 46 PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0); |
47 | 47 |
48 // Store a common id in the SO_PEEK_OFF option (which we don't use since we | 48 // Store a common id in the SO_PEEK_OFF option (which we don't use since we |
49 // don't peak) as a way of determining later if two sockets are connected to | 49 // don't peek) as a way of determining later if two sockets are connected to |
50 // each other. | 50 // each other. |
51 int identifier = base::RandInt(kint32min, kint32max); | 51 int identifier = base::RandInt(kint32min, kint32max); |
52 setsockopt(fds[0], SOL_SOCKET, SO_PEEK_OFF, &identifier, sizeof(identifier)); | 52 setsockopt(fds[0], SOL_SOCKET, SO_PEEK_OFF, &identifier, sizeof(identifier)); |
53 setsockopt(fds[1], SOL_SOCKET, SO_PEEK_OFF, &identifier, sizeof(identifier)); | 53 setsockopt(fds[1], SOL_SOCKET, SO_PEEK_OFF, &identifier, sizeof(identifier)); |
54 | 54 |
| 55 // Set the ends to nonblocking. |
55 PCHECK(fcntl(fds[0], F_SETFL, O_NONBLOCK) == 0); | 56 PCHECK(fcntl(fds[0], F_SETFL, O_NONBLOCK) == 0); |
56 PCHECK(fcntl(fds[1], F_SETFL, O_NONBLOCK) == 0); | 57 if (!client_is_blocking) |
| 58 PCHECK(fcntl(fds[1], F_SETFL, O_NONBLOCK) == 0); |
57 | 59 |
58 #if defined(OS_MACOSX) | 60 #if defined(OS_MACOSX) |
59 // This turns off |SIGPIPE| when writing to a closed socket (causing it to | 61 // This turns off |SIGPIPE| when writing to a closed socket (causing it to |
60 // fail with |EPIPE| instead). On Linux, we have to use |send...()| with | 62 // fail with |EPIPE| instead). On Linux, we have to use |send...()| with |
61 // |MSG_NOSIGNAL| -- which is not supported on Mac -- instead. | 63 // |MSG_NOSIGNAL| -- which is not supported on Mac -- instead. |
62 int no_sigpipe = 1; | 64 int no_sigpipe = 1; |
63 PCHECK(setsockopt(fds[0], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe, | 65 PCHECK(setsockopt(fds[0], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe, |
64 sizeof(no_sigpipe)) == 0); | 66 sizeof(no_sigpipe)) == 0); |
65 PCHECK(setsockopt(fds[1], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe, | 67 PCHECK(setsockopt(fds[1], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe, |
66 sizeof(no_sigpipe)) == 0); | 68 sizeof(no_sigpipe)) == 0); |
67 #endif // defined(OS_MACOSX) | 69 #endif // defined(OS_MACOSX) |
68 | 70 |
69 server_handle_.reset(PlatformHandle(fds[0])); | 71 server_handle_.reset(PlatformHandle(fds[0])); |
70 DCHECK(server_handle_.is_valid()); | 72 DCHECK(server_handle_.is_valid()); |
71 client_handle_.reset(PlatformHandle(fds[1])); | 73 client_handle_.reset(PlatformHandle(fds[1])); |
72 DCHECK(client_handle_.is_valid()); | 74 DCHECK(client_handle_.is_valid()); |
73 } | 75 } |
74 | 76 |
75 // static | 77 // static |
76 ScopedPlatformHandle PlatformChannelPair::PassClientHandleFromParentProcess( | 78 ScopedPlatformHandle PlatformChannelPair::PassClientHandleFromParentProcess( |
77 const base::CommandLine& command_line) { | 79 const base::CommandLine& command_line) { |
78 std::string client_fd_string = | 80 std::string client_fd_string = |
79 command_line.GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch); | 81 command_line.GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch); |
| 82 return PassClientHandleFromParentProcessFromString(client_fd_string); |
| 83 } |
| 84 |
| 85 ScopedPlatformHandle |
| 86 PlatformChannelPair::PassClientHandleFromParentProcessFromString( |
| 87 const std::string& value) { |
80 int client_fd = -1; | 88 int client_fd = -1; |
81 if (client_fd_string.empty() || | 89 if (value.empty() || |
82 !base::StringToInt(client_fd_string, &client_fd) || | 90 !base::StringToInt(value, &client_fd) || |
83 client_fd < base::GlobalDescriptors::kBaseDescriptor) { | 91 client_fd < base::GlobalDescriptors::kBaseDescriptor) { |
84 LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch; | 92 LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch; |
85 return ScopedPlatformHandle(); | 93 return ScopedPlatformHandle(); |
86 } | 94 } |
87 | 95 |
88 return ScopedPlatformHandle(PlatformHandle(client_fd)); | 96 return ScopedPlatformHandle(PlatformHandle(client_fd)); |
89 } | 97 } |
90 | 98 |
91 void PlatformChannelPair::PrepareToPassClientHandleToChildProcess( | 99 void PlatformChannelPair::PrepareToPassClientHandleToChildProcess( |
92 base::CommandLine* command_line, | 100 base::CommandLine* command_line, |
93 base::FileHandleMappingVector* handle_passing_info) const { | 101 base::FileHandleMappingVector* handle_passing_info) const { |
94 DCHECK(command_line); | 102 DCHECK(command_line); |
| 103 |
| 104 // Log a warning if the command line already has the switch, but "clobber" it |
| 105 // anyway, since it's reasonably likely that all the switches were just copied |
| 106 // from the parent. |
| 107 LOG_IF(WARNING, command_line->HasSwitch(kMojoPlatformChannelHandleSwitch)) |
| 108 << "Child command line already has switch --" |
| 109 << kMojoPlatformChannelHandleSwitch << "=" |
| 110 << command_line->GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch); |
| 111 // (Any existing switch won't actually be removed from the command line, but |
| 112 // the last one appended takes precedence.) |
| 113 command_line->AppendSwitchASCII( |
| 114 kMojoPlatformChannelHandleSwitch, |
| 115 PrepareToPassClientHandleToChildProcessAsString(handle_passing_info)); |
| 116 } |
| 117 |
| 118 std::string |
| 119 PlatformChannelPair::PrepareToPassClientHandleToChildProcessAsString( |
| 120 HandlePassingInformation* handle_passing_info) const { |
95 DCHECK(handle_passing_info); | 121 DCHECK(handle_passing_info); |
96 // This is an arbitrary sanity check. (Note that this guarantees that the loop | 122 // This is an arbitrary sanity check. (Note that this guarantees that the loop |
97 // below will terminate sanely.) | 123 // below will terminate sanely.) |
98 CHECK_LT(handle_passing_info->size(), 1000u); | 124 CHECK_LT(handle_passing_info->size(), 1000u); |
99 | 125 |
100 DCHECK(client_handle_.is_valid()); | 126 DCHECK(client_handle_.is_valid()); |
101 | 127 |
102 // Find a suitable FD to map our client handle to in the child process. | 128 // Find a suitable FD to map our client handle to in the child process. |
103 // This has quadratic time complexity in the size of |*handle_passing_info|, | 129 // This has quadratic time complexity in the size of |*handle_passing_info|, |
104 // but |*handle_passing_info| should be very small (usually/often empty). | 130 // but |*handle_passing_info| should be very small (usually/often empty). |
105 int target_fd = base::GlobalDescriptors::kBaseDescriptor; | 131 int target_fd = base::GlobalDescriptors::kBaseDescriptor; |
106 while (IsTargetDescriptorUsed(*handle_passing_info, target_fd)) | 132 while (IsTargetDescriptorUsed(*handle_passing_info, target_fd)) |
107 target_fd++; | 133 target_fd++; |
108 | 134 |
109 handle_passing_info->push_back( | 135 handle_passing_info->push_back( |
110 std::pair<int, int>(client_handle_.get().fd, target_fd)); | 136 std::pair<int, int>(client_handle_.get().fd, target_fd)); |
111 // Log a warning if the command line already has the switch, but "clobber" it | 137 return base::IntToString(target_fd); |
112 // anyway, since it's reasonably likely that all the switches were just copied | |
113 // from the parent. | |
114 LOG_IF(WARNING, command_line->HasSwitch(kMojoPlatformChannelHandleSwitch)) | |
115 << "Child command line already has switch --" | |
116 << kMojoPlatformChannelHandleSwitch << "=" | |
117 << command_line->GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch); | |
118 // (Any existing switch won't actually be removed from the command line, but | |
119 // the last one appended takes precedence.) | |
120 command_line->AppendSwitchASCII(kMojoPlatformChannelHandleSwitch, | |
121 base::IntToString(target_fd)); | |
122 } | 138 } |
123 | 139 |
124 } // namespace edk | 140 } // namespace edk |
125 } // namespace mojo | 141 } // namespace mojo |
OLD | NEW |