Home | History | Annotate | Download | only in embedder
      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/edk/embedder/platform_channel_pair.h"
      6 
      7 #include <fcntl.h>
      8 #include <stddef.h>
      9 #include <stdint.h>
     10 #include <sys/types.h>
     11 #include <unistd.h>
     12 
     13 #include <limits>
     14 
     15 #include "base/command_line.h"
     16 #include "base/logging.h"
     17 #include "base/posix/global_descriptors.h"
     18 #include "base/rand_util.h"
     19 #include "base/strings/string_number_conversions.h"
     20 #include "build/build_config.h"
     21 #include "mojo/edk/embedder/platform_handle.h"
     22 
     23 #if !defined(OS_NACL_SFI)
     24 #include <sys/socket.h>
     25 #else
     26 #include "native_client/src/public/imc_syscalls.h"
     27 #endif
     28 
     29 #if !defined(SO_PEEK_OFF)
     30 #define SO_PEEK_OFF 42
     31 #endif
     32 
     33 namespace mojo {
     34 namespace edk {
     35 
     36 namespace {
     37 
     38 bool IsTargetDescriptorUsed(
     39     const base::FileHandleMappingVector& file_handle_mapping,
     40     int target_fd) {
     41   for (size_t i = 0; i < file_handle_mapping.size(); i++) {
     42     if (file_handle_mapping[i].second == target_fd)
     43       return true;
     44   }
     45   return false;
     46 }
     47 
     48 }  // namespace
     49 
     50 PlatformChannelPair::PlatformChannelPair(bool client_is_blocking) {
     51   // Create the Unix domain socket.
     52   int fds[2];
     53   // TODO(vtl): Maybe fail gracefully if |socketpair()| fails.
     54 
     55 #if defined(OS_NACL_SFI)
     56   PCHECK(imc_socketpair(fds) == 0);
     57 #else
     58   PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0);
     59 
     60   // Set the ends to nonblocking.
     61   PCHECK(fcntl(fds[0], F_SETFL, O_NONBLOCK) == 0);
     62   if (!client_is_blocking)
     63     PCHECK(fcntl(fds[1], F_SETFL, O_NONBLOCK) == 0);
     64 
     65 #if defined(OS_MACOSX)
     66   // This turns off |SIGPIPE| when writing to a closed socket (causing it to
     67   // fail with |EPIPE| instead). On Linux, we have to use |send...()| with
     68   // |MSG_NOSIGNAL| -- which is not supported on Mac -- instead.
     69   int no_sigpipe = 1;
     70   PCHECK(setsockopt(fds[0], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe,
     71                     sizeof(no_sigpipe)) == 0);
     72   PCHECK(setsockopt(fds[1], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe,
     73                     sizeof(no_sigpipe)) == 0);
     74 #endif  // defined(OS_MACOSX)
     75 #endif  // defined(OS_NACL_SFI)
     76 
     77   server_handle_.reset(PlatformHandle(fds[0]));
     78   DCHECK(server_handle_.is_valid());
     79   client_handle_.reset(PlatformHandle(fds[1]));
     80   DCHECK(client_handle_.is_valid());
     81 }
     82 
     83 // static
     84 ScopedPlatformHandle PlatformChannelPair::PassClientHandleFromParentProcess(
     85     const base::CommandLine& command_line) {
     86   std::string client_fd_string =
     87       command_line.GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch);
     88   return PassClientHandleFromParentProcessFromString(client_fd_string);
     89 }
     90 
     91 ScopedPlatformHandle
     92 PlatformChannelPair::PassClientHandleFromParentProcessFromString(
     93     const std::string& value) {
     94   int client_fd = -1;
     95   if (value.empty() ||
     96       !base::StringToInt(value, &client_fd) ||
     97       client_fd < base::GlobalDescriptors::kBaseDescriptor) {
     98     LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch;
     99     return ScopedPlatformHandle();
    100   }
    101 
    102   return ScopedPlatformHandle(PlatformHandle(client_fd));
    103 }
    104 
    105 void PlatformChannelPair::PrepareToPassClientHandleToChildProcess(
    106     base::CommandLine* command_line,
    107     base::FileHandleMappingVector* handle_passing_info) const {
    108   DCHECK(command_line);
    109 
    110   // Log a warning if the command line already has the switch, but "clobber" it
    111   // anyway, since it's reasonably likely that all the switches were just copied
    112   // from the parent.
    113   LOG_IF(WARNING, command_line->HasSwitch(kMojoPlatformChannelHandleSwitch))
    114       << "Child command line already has switch --"
    115       << kMojoPlatformChannelHandleSwitch << "="
    116       << command_line->GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch);
    117   // (Any existing switch won't actually be removed from the command line, but
    118   // the last one appended takes precedence.)
    119   command_line->AppendSwitchASCII(
    120       kMojoPlatformChannelHandleSwitch,
    121       PrepareToPassClientHandleToChildProcessAsString(handle_passing_info));
    122 }
    123 
    124 std::string
    125 PlatformChannelPair::PrepareToPassClientHandleToChildProcessAsString(
    126       HandlePassingInformation* handle_passing_info) const {
    127   DCHECK(handle_passing_info);
    128   // This is an arbitrary sanity check. (Note that this guarantees that the loop
    129   // below will terminate sanely.)
    130   CHECK_LT(handle_passing_info->size(), 1000u);
    131 
    132   DCHECK(client_handle_.is_valid());
    133 
    134   // Find a suitable FD to map our client handle to in the child process.
    135   // This has quadratic time complexity in the size of |*handle_passing_info|,
    136   // but |*handle_passing_info| should be very small (usually/often empty).
    137   int target_fd = base::GlobalDescriptors::kBaseDescriptor;
    138   while (IsTargetDescriptorUsed(*handle_passing_info, target_fd))
    139     target_fd++;
    140 
    141   handle_passing_info->push_back(
    142       std::pair<int, int>(client_handle_.get().handle, target_fd));
    143   return base::IntToString(target_fd);
    144 }
    145 
    146 }  // namespace edk
    147 }  // namespace mojo
    148