• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
IsTargetDescriptorUsed(const base::FileHandleMappingVector & file_handle_mapping,int target_fd)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 
PlatformServerChannelPosix(const std::string & name)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 
~PlatformServerChannelPosix()68 PlatformServerChannelPosix::~PlatformServerChannelPosix() {
69   client_handle_.CloseIfNecessary();
70 }
71 
72 scoped_ptr<PlatformClientChannel>
CreateClientChannel()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 
GetDataNeededToPassClientChannelToChildProcess(CommandLine * command_line,base::FileHandleMappingVector * file_handle_mapping) const86 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 
ChildProcessLaunched()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
Create(const std::string & name)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>
CreateFromParentProcess(const CommandLine & command_line)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