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. 4 5#include "mojo/embedder/platform_channel_pair.h" 6 7#include <fcntl.h> 8#include <sys/socket.h> 9#include <sys/types.h> 10#include <unistd.h> 11 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" 18 19namespace mojo { 20namespace embedder { 21 22namespace { 23 24bool IsTargetDescriptorUsed( 25 const base::FileHandleMappingVector& file_handle_mapping, 26 int target_fd) { 27 for (size_t i = 0; i < file_handle_mapping.size(); i++) { 28 if (file_handle_mapping[i].second == target_fd) 29 return true; 30 } 31 return false; 32} 33 34} // namespace 35 36PlatformChannelPair::PlatformChannelPair() { 37 // Create the Unix domain socket and set the ends to nonblocking. 38 int fds[2]; 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); 43 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. 48 int no_sigpipe = 1; 49 PCHECK( 50 setsockopt( 51 fds[0], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe, sizeof(no_sigpipe)) == 52 0); 53 PCHECK( 54 setsockopt( 55 fds[1], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe, sizeof(no_sigpipe)) == 56 0); 57#endif // defined(OS_MACOSX) 58 59 server_handle_.reset(PlatformHandle(fds[0])); 60 DCHECK(server_handle_.is_valid()); 61 client_handle_.reset(PlatformHandle(fds[1])); 62 DCHECK(client_handle_.is_valid()); 63} 64 65// static 66ScopedPlatformHandle PlatformChannelPair::PassClientHandleFromParentProcess( 67 const base::CommandLine& command_line) { 68 std::string client_fd_string = 69 command_line.GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch); 70 int client_fd = -1; 71 if (client_fd_string.empty() || 72 !base::StringToInt(client_fd_string, &client_fd) || 73 client_fd < base::GlobalDescriptors::kBaseDescriptor) { 74 LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch; 75 return ScopedPlatformHandle(); 76 } 77 78 return ScopedPlatformHandle(PlatformHandle(client_fd)); 79} 80 81void PlatformChannelPair::PrepareToPassClientHandleToChildProcess( 82 base::CommandLine* command_line, 83 base::FileHandleMappingVector* handle_passing_info) const { 84 DCHECK(command_line); 85 DCHECK(handle_passing_info); 86 // This is an arbitrary sanity check. (Note that this guarantees that the loop 87 // below will terminate sanely.) 88 CHECK_LT(handle_passing_info->size(), 1000u); 89 90 DCHECK(client_handle_.is_valid()); 91 92 // Find a suitable FD to map our client handle to in the child process. 93 // This has quadratic time complexity in the size of |*handle_passing_info|, 94 // but |*handle_passing_info| should be very small (usually/often empty). 95 int target_fd = base::GlobalDescriptors::kBaseDescriptor; 96 while (IsTargetDescriptorUsed(*handle_passing_info, target_fd)) 97 target_fd++; 98 99 handle_passing_info->push_back( 100 std::pair<int, int>(client_handle_.get().fd, target_fd)); 101 // Log a warning if the command line already has the switch, but "clobber" it 102 // anyway, since it's reasonably likely that all the switches were just copied 103 // from the parent. 104 LOG_IF(WARNING, command_line->HasSwitch(kMojoPlatformChannelHandleSwitch)) 105 << "Child command line already has switch --" 106 << kMojoPlatformChannelHandleSwitch << "=" 107 << command_line->GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch); 108 // (Any existing switch won't actually be removed from the command line, but 109 // the last one appended takes precedence.) 110 command_line->AppendSwitchASCII(kMojoPlatformChannelHandleSwitch, 111 base::IntToString(target_fd)); 112} 113 114} // namespace embedder 115} // namespace mojo 116