Home | History | Annotate | Download | only in system
      1 // Copyright 2013 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/system/platform_channel.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/compiler_specific.h"
     14 #include "base/logging.h"
     15 #include "base/posix/global_descriptors.h"
     16 #include "base/strings/string_number_conversions.h"
     17 
     18 namespace mojo {
     19 namespace system {
     20 
     21 namespace {
     22 
     23 const char kMojoChannelDescriptorSwitch[] = "mojo-channel-descriptor";
     24 
     25 bool IsTargetDescriptorUsed(
     26     const base::FileHandleMappingVector& file_handle_mapping,
     27     int target_fd) {
     28   for (size_t i = 0; i < file_handle_mapping.size(); i++) {
     29     if (file_handle_mapping[i].second == target_fd)
     30       return true;
     31   }
     32   return false;
     33 }
     34 
     35 class PlatformServerChannelPosix : public PlatformServerChannel {
     36  public:
     37   PlatformServerChannelPosix(const std::string& name);
     38   virtual ~PlatformServerChannelPosix();
     39 
     40   // |PlatformServerChannel| implementation:
     41   virtual scoped_ptr<PlatformClientChannel> CreateClientChannel() OVERRIDE;
     42   virtual void GetDataNeededToPassClientChannelToChildProcess(
     43       CommandLine* command_line,
     44       base::FileHandleMappingVector* file_handle_mapping) const OVERRIDE;
     45   virtual void ChildProcessLaunched() OVERRIDE;
     46 
     47  private:
     48   PlatformChannelHandle client_handle_;
     49 
     50   DISALLOW_COPY_AND_ASSIGN(PlatformServerChannelPosix);
     51 };
     52 
     53 PlatformServerChannelPosix::PlatformServerChannelPosix(
     54     const std::string& name)
     55     : PlatformServerChannel(name) {
     56   // Create the Unix domain socket and set the ends to nonblocking.
     57   int fds[2];
     58   PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0);
     59   PCHECK(fcntl(fds[0], F_SETFL, O_NONBLOCK) == 0);
     60   PCHECK(fcntl(fds[1], F_SETFL, O_NONBLOCK) == 0);
     61 
     62   mutable_handle()->fd = fds[0];
     63   DCHECK(is_valid());
     64   client_handle_.fd = fds[1];
     65   DCHECK(client_handle_.is_valid());
     66 }
     67 
     68 PlatformServerChannelPosix::~PlatformServerChannelPosix() {
     69   client_handle_.CloseIfNecessary();
     70 }
     71 
     72 scoped_ptr<PlatformClientChannel>
     73     PlatformServerChannelPosix::CreateClientChannel() {
     74   if (!client_handle_.is_valid()) {
     75     NOTREACHED();
     76     return scoped_ptr<PlatformClientChannel>();
     77   }
     78 
     79   scoped_ptr<PlatformClientChannel> rv =
     80       PlatformClientChannel::CreateFromHandle(client_handle_);
     81   DCHECK(rv->is_valid());
     82   client_handle_ = PlatformChannelHandle();
     83   return rv.Pass();
     84 }
     85 
     86 void PlatformServerChannelPosix::GetDataNeededToPassClientChannelToChildProcess(
     87     CommandLine* command_line,
     88     base::FileHandleMappingVector* file_handle_mapping) const {
     89   DCHECK(command_line);
     90   DCHECK(file_handle_mapping);
     91   // This is an arbitrary sanity check. (Note that this guarantees that the loop
     92   // below will terminate sanely.)
     93   CHECK_LT(file_handle_mapping->size(), 1000u);
     94 
     95   DCHECK(client_handle_.is_valid());
     96 
     97   // Find a suitable FD to map our client handle to in the child process.
     98   // This has quadratic time complexity in the size of |*file_handle_mapping|,
     99   // but |*file_handle_mapping| should be very small (usually/often empty).
    100   int target_fd = base::GlobalDescriptors::kBaseDescriptor;
    101   while (IsTargetDescriptorUsed(*file_handle_mapping, target_fd))
    102     target_fd++;
    103 
    104   file_handle_mapping->push_back(std::pair<int, int>(client_handle_.fd,
    105                                                      target_fd));
    106   // Log a warning if the command line already has the switch, but "clobber" it
    107   // anyway, since it's reasonably likely that all the switches were just copied
    108   // from the parent.
    109   LOG_IF(WARNING, command_line->HasSwitch(kMojoChannelDescriptorSwitch))
    110       << "Child command line already has switch --"
    111       << kMojoChannelDescriptorSwitch << "="
    112       << command_line->GetSwitchValueASCII(kMojoChannelDescriptorSwitch);
    113   // (Any existing switch won't actually be removed from the command line, but
    114   // the last one appended takes precedence.)
    115   command_line->AppendSwitchASCII(kMojoChannelDescriptorSwitch,
    116                                   base::IntToString(target_fd));
    117 }
    118 
    119 void PlatformServerChannelPosix::ChildProcessLaunched() {
    120   DCHECK(client_handle_.is_valid());
    121   client_handle_.CloseIfNecessary();
    122 }
    123 
    124 }  // namespace
    125 
    126 // -----------------------------------------------------------------------------
    127 
    128 // Static factory method declared in platform_channel.h.
    129 // static
    130 scoped_ptr<PlatformServerChannel> PlatformServerChannel::Create(
    131     const std::string& name) {
    132   return scoped_ptr<PlatformServerChannel>(
    133       new PlatformServerChannelPosix(name));
    134 }
    135 
    136 // -----------------------------------------------------------------------------
    137 
    138 // Static factory method declared in platform_channel.h.
    139 // static
    140 scoped_ptr<PlatformClientChannel>
    141     PlatformClientChannel::CreateFromParentProcess(
    142         const CommandLine& command_line) {
    143   std::string client_fd_string =
    144       command_line.GetSwitchValueASCII(kMojoChannelDescriptorSwitch);
    145   int client_fd = -1;
    146   if (client_fd_string.empty() ||
    147       !base::StringToInt(client_fd_string, &client_fd) ||
    148       client_fd < base::GlobalDescriptors::kBaseDescriptor) {
    149     LOG(ERROR) << "Missing or invalid --" << kMojoChannelDescriptorSwitch;
    150     return scoped_ptr<PlatformClientChannel>();
    151   }
    152 
    153   return CreateFromHandle(PlatformChannelHandle(client_fd));
    154 }
    155 
    156 }  // namespace system
    157 }  // namespace mojo
    158