| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "mojo/system/platform_channel.h" |
| |
| #include <fcntl.h> |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include "base/command_line.h" |
| #include "base/compiler_specific.h" |
| #include "base/logging.h" |
| #include "base/posix/global_descriptors.h" |
| #include "base/strings/string_number_conversions.h" |
| |
| namespace mojo { |
| namespace system { |
| |
| namespace { |
| |
| const char kMojoChannelDescriptorSwitch[] = "mojo-channel-descriptor"; |
| |
| bool IsTargetDescriptorUsed( |
| const base::FileHandleMappingVector& file_handle_mapping, |
| int target_fd) { |
| for (size_t i = 0; i < file_handle_mapping.size(); i++) { |
| if (file_handle_mapping[i].second == target_fd) |
| return true; |
| } |
| return false; |
| } |
| |
| class PlatformServerChannelPosix : public PlatformServerChannel { |
| public: |
| PlatformServerChannelPosix(const std::string& name); |
| virtual ~PlatformServerChannelPosix(); |
| |
| // |PlatformServerChannel| implementation: |
| virtual scoped_ptr<PlatformClientChannel> CreateClientChannel() OVERRIDE; |
| virtual void GetDataNeededToPassClientChannelToChildProcess( |
| CommandLine* command_line, |
| base::FileHandleMappingVector* file_handle_mapping) const OVERRIDE; |
| virtual void ChildProcessLaunched() OVERRIDE; |
| |
| private: |
| PlatformChannelHandle client_handle_; |
| |
| DISALLOW_COPY_AND_ASSIGN(PlatformServerChannelPosix); |
| }; |
| |
| PlatformServerChannelPosix::PlatformServerChannelPosix( |
| const std::string& name) |
| : PlatformServerChannel(name) { |
| // Create the Unix domain socket and set the ends to nonblocking. |
| int fds[2]; |
| PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0); |
| PCHECK(fcntl(fds[0], F_SETFL, O_NONBLOCK) == 0); |
| PCHECK(fcntl(fds[1], F_SETFL, O_NONBLOCK) == 0); |
| |
| mutable_handle()->fd = fds[0]; |
| DCHECK(is_valid()); |
| client_handle_.fd = fds[1]; |
| DCHECK(client_handle_.is_valid()); |
| } |
| |
| PlatformServerChannelPosix::~PlatformServerChannelPosix() { |
| client_handle_.CloseIfNecessary(); |
| } |
| |
| scoped_ptr<PlatformClientChannel> |
| PlatformServerChannelPosix::CreateClientChannel() { |
| if (!client_handle_.is_valid()) { |
| NOTREACHED(); |
| return scoped_ptr<PlatformClientChannel>(); |
| } |
| |
| scoped_ptr<PlatformClientChannel> rv = |
| PlatformClientChannel::CreateFromHandle(client_handle_); |
| DCHECK(rv->is_valid()); |
| client_handle_ = PlatformChannelHandle(); |
| return rv.Pass(); |
| } |
| |
| void PlatformServerChannelPosix::GetDataNeededToPassClientChannelToChildProcess( |
| CommandLine* command_line, |
| base::FileHandleMappingVector* file_handle_mapping) const { |
| DCHECK(command_line); |
| DCHECK(file_handle_mapping); |
| // This is an arbitrary sanity check. (Note that this guarantees that the loop |
| // below will terminate sanely.) |
| CHECK_LT(file_handle_mapping->size(), 1000u); |
| |
| DCHECK(client_handle_.is_valid()); |
| |
| // Find a suitable FD to map our client handle to in the child process. |
| // This has quadratic time complexity in the size of |*file_handle_mapping|, |
| // but |*file_handle_mapping| should be very small (usually/often empty). |
| int target_fd = base::GlobalDescriptors::kBaseDescriptor; |
| while (IsTargetDescriptorUsed(*file_handle_mapping, target_fd)) |
| target_fd++; |
| |
| file_handle_mapping->push_back(std::pair<int, int>(client_handle_.fd, |
| target_fd)); |
| // Log a warning if the command line already has the switch, but "clobber" it |
| // anyway, since it's reasonably likely that all the switches were just copied |
| // from the parent. |
| LOG_IF(WARNING, command_line->HasSwitch(kMojoChannelDescriptorSwitch)) |
| << "Child command line already has switch --" |
| << kMojoChannelDescriptorSwitch << "=" |
| << command_line->GetSwitchValueASCII(kMojoChannelDescriptorSwitch); |
| // (Any existing switch won't actually be removed from the command line, but |
| // the last one appended takes precedence.) |
| command_line->AppendSwitchASCII(kMojoChannelDescriptorSwitch, |
| base::IntToString(target_fd)); |
| } |
| |
| void PlatformServerChannelPosix::ChildProcessLaunched() { |
| DCHECK(client_handle_.is_valid()); |
| client_handle_.CloseIfNecessary(); |
| } |
| |
| } // namespace |
| |
| // ----------------------------------------------------------------------------- |
| |
| // Static factory method declared in platform_channel.h. |
| // static |
| scoped_ptr<PlatformServerChannel> PlatformServerChannel::Create( |
| const std::string& name) { |
| return scoped_ptr<PlatformServerChannel>( |
| new PlatformServerChannelPosix(name)); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| // Static factory method declared in platform_channel.h. |
| // static |
| scoped_ptr<PlatformClientChannel> |
| PlatformClientChannel::CreateFromParentProcess( |
| const CommandLine& command_line) { |
| std::string client_fd_string = |
| command_line.GetSwitchValueASCII(kMojoChannelDescriptorSwitch); |
| int client_fd = -1; |
| if (client_fd_string.empty() || |
| !base::StringToInt(client_fd_string, &client_fd) || |
| client_fd < base::GlobalDescriptors::kBaseDescriptor) { |
| LOG(ERROR) << "Missing or invalid --" << kMojoChannelDescriptorSwitch; |
| return scoped_ptr<PlatformClientChannel>(); |
| } |
| |
| return CreateFromHandle(PlatformChannelHandle(client_fd)); |
| } |
| |
| } // namespace system |
| } // namespace mojo |