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
3 // found in the LICENSE file.
5 #include "mojo/embedder/platform_channel_pair.h"
8 #include <sys/socket.h>
12 #include "base/command_line.h"
13 #include "base/logging.h"
14 #include "base/posix/global_descriptors.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "build/build_config.h"
17 #include "mojo/embedder/platform_handle.h"
24 bool IsTargetDescriptorUsed(
25 const base::FileHandleMappingVector
& file_handle_mapping
,
27 for (size_t i
= 0; i
< file_handle_mapping
.size(); i
++) {
28 if (file_handle_mapping
[i
].second
== target_fd
)
36 PlatformChannelPair::PlatformChannelPair() {
37 // Create the Unix domain socket and set the ends to nonblocking.
39 // TODO(vtl): Maybe fail gracefully if |socketpair()| fails.
40 PCHECK(socketpair(AF_UNIX
, SOCK_STREAM
, 0, fds
) == 0);
41 PCHECK(fcntl(fds
[0], F_SETFL
, O_NONBLOCK
) == 0);
42 PCHECK(fcntl(fds
[1], F_SETFL
, O_NONBLOCK
) == 0);
44 #if defined(OS_MACOSX)
45 // This turns off |SIGPIPE| when writing to a closed socket (causing it to
46 // fail with |EPIPE| instead). On Linux, we have to use |send...()| with
47 // |MSG_NOSIGNAL| -- which is not supported on Mac -- instead.
49 PCHECK(setsockopt(fds
[0], SOL_SOCKET
, SO_NOSIGPIPE
, &no_sigpipe
,
50 sizeof(no_sigpipe
)) == 0);
51 PCHECK(setsockopt(fds
[1], SOL_SOCKET
, SO_NOSIGPIPE
, &no_sigpipe
,
52 sizeof(no_sigpipe
)) == 0);
53 #endif // defined(OS_MACOSX)
55 server_handle_
.reset(PlatformHandle(fds
[0]));
56 DCHECK(server_handle_
.is_valid());
57 client_handle_
.reset(PlatformHandle(fds
[1]));
58 DCHECK(client_handle_
.is_valid());
62 ScopedPlatformHandle
PlatformChannelPair::PassClientHandleFromParentProcess(
63 const base::CommandLine
& command_line
) {
64 std::string client_fd_string
=
65 command_line
.GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch
);
67 if (client_fd_string
.empty() ||
68 !base::StringToInt(client_fd_string
, &client_fd
) ||
69 client_fd
< base::GlobalDescriptors::kBaseDescriptor
) {
70 LOG(ERROR
) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch
;
71 return ScopedPlatformHandle();
74 return ScopedPlatformHandle(PlatformHandle(client_fd
));
77 void PlatformChannelPair::PrepareToPassClientHandleToChildProcess(
78 base::CommandLine
* command_line
,
79 base::FileHandleMappingVector
* handle_passing_info
) const {
81 DCHECK(handle_passing_info
);
82 // This is an arbitrary sanity check. (Note that this guarantees that the loop
83 // below will terminate sanely.)
84 CHECK_LT(handle_passing_info
->size(), 1000u);
86 DCHECK(client_handle_
.is_valid());
88 // Find a suitable FD to map our client handle to in the child process.
89 // This has quadratic time complexity in the size of |*handle_passing_info|,
90 // but |*handle_passing_info| should be very small (usually/often empty).
91 int target_fd
= base::GlobalDescriptors::kBaseDescriptor
;
92 while (IsTargetDescriptorUsed(*handle_passing_info
, target_fd
))
95 handle_passing_info
->push_back(std::pair
<int, int>(client_handle_
.get().fd
,
97 // Log a warning if the command line already has the switch, but "clobber" it
98 // anyway, since it's reasonably likely that all the switches were just copied
100 LOG_IF(WARNING
, command_line
->HasSwitch(kMojoPlatformChannelHandleSwitch
))
101 << "Child command line already has switch --"
102 << kMojoPlatformChannelHandleSwitch
<< "="
103 << command_line
->GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch
);
104 // (Any existing switch won't actually be removed from the command line, but
105 // the last one appended takes precedence.)
106 command_line
->AppendSwitchASCII(kMojoPlatformChannelHandleSwitch
,
107 base::IntToString(target_fd
));
110 } // namespace embedder