Commit 6a6dca0f authored by Ken Rockot's avatar Ken Rockot Committed by Commit Bot

Mojo: Delete EDK internal channel endpoint types

Deletes PlatformChannelPair, NamedPlatformChannelPair, and
NamedPlatformHandle types from the EDK. These are superceded by
equivalent types in the public platform support API.

TBR=jcivelli@chromium.org

Bug: 753541
Change-Id: Ic1982aac93f0b6a78730497fc04dc44bb128cef0
Reviewed-on: https://chromium-review.googlesource.com/1109259
Commit-Queue: Ken Rockot <rockot@chromium.org>
Reviewed-by: default avatarKen Rockot <rockot@chromium.org>
Cr-Commit-Position: refs/heads/master@{#569707}
parent 3c7f51e9
......@@ -65,10 +65,6 @@ template("core_impl_source_set") {
public = [
"embedder/configuration.h",
"embedder/named_platform_channel_pair.h",
"embedder/named_platform_handle.h",
"embedder/named_platform_handle_utils.h",
"embedder/platform_channel_pair.h",
"embedder/platform_handle.h",
"embedder/process_error_callback.h",
"embedder/scoped_platform_handle.h",
......@@ -96,16 +92,7 @@ template("core_impl_source_set") {
"system/user_message_impl.h",
]
if (is_win) {
public += [ "embedder/named_platform_handle_win.h" ]
} else if (is_posix || is_fuchsia) {
public += [ "embedder/named_platform_handle_posix.h" ]
}
sources = [
"embedder/named_platform_handle_utils_win.cc",
"embedder/platform_channel_pair.cc",
"embedder/platform_channel_pair_win.cc",
"embedder/platform_handle.cc",
"system/atomic_flag.h",
"system/broker.h",
......@@ -149,28 +136,18 @@ template("core_impl_source_set") {
]
if (is_fuchsia) {
sources += [
"embedder/named_platform_handle_utils_fuchsia.cc",
"embedder/platform_channel_pair_fuchsia.cc",
"system/channel_fuchsia.cc",
]
sources += [ "system/channel_fuchsia.cc" ]
public_deps += [ "//third_party/fuchsia-sdk:fdio" ]
}
if (is_posix) {
sources += [ "embedder/platform_channel_pair_posix.cc" ]
if (!is_nacl || is_nacl_nonsfi) {
sources += [
"system/broker_posix.cc",
"system/channel_posix.cc",
]
}
if (!is_nacl) {
sources += [ "embedder/named_platform_handle_utils_posix.cc" ]
}
}
if (is_mac && !is_ios) {
......@@ -197,7 +174,6 @@ template("core_impl_source_set") {
deps += [ "//third_party/ashmem" ]
}
if (!is_nacl) {
sources += [ "embedder/named_platform_channel_pair.cc" ]
deps += [ "//crypto" ]
}
......
// Copyright 2016 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/edk/embedder/named_platform_channel_pair.h"
#include <memory>
#include <string>
#include <utility>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/windows_version.h"
#include "mojo/edk/embedder/named_platform_handle_utils.h"
#include "mojo/edk/embedder/platform_handle.h"
#if defined(OS_WIN)
#include <windows.h>
#endif
namespace mojo {
namespace edk {
namespace {
const char kMojoNamedPlatformChannelPipeSwitch[] =
"mojo-named-platform-channel-pipe";
#if defined(OS_WIN)
std::wstring GeneratePipeName(
const NamedPlatformChannelPair::Options& options) {
return base::StringPrintf(L"%u.%u.%I64u", GetCurrentProcessId(),
GetCurrentThreadId(), base::RandUint64());
}
#else
std::string GeneratePipeName(const NamedPlatformChannelPair::Options& options) {
return options.socket_dir
.AppendASCII(base::NumberToString(base::RandUint64()))
.value();
}
#endif
} // namespace
NamedPlatformChannelPair::NamedPlatformChannelPair(
const NamedPlatformChannelPair::Options& options)
: pipe_handle_(GeneratePipeName(options)) {
CreateServerHandleOptions server_handle_options;
#if defined(OS_WIN)
server_handle_options.security_descriptor = options.security_descriptor;
server_handle_options.enforce_uniqueness = true;
#endif
server_handle_ = CreateServerHandle(pipe_handle_, server_handle_options);
PCHECK(server_handle_.is_valid());
}
NamedPlatformChannelPair::~NamedPlatformChannelPair() {}
ScopedInternalPlatformHandle NamedPlatformChannelPair::PassServerHandle() {
return std::move(server_handle_);
}
// static
ScopedInternalPlatformHandle
NamedPlatformChannelPair::PassClientHandleFromParentProcess(
const base::CommandLine& command_line) {
// In order to support passing the pipe name on the command line, the pipe
// handle is lazily created from the pipe name when requested.
NamedPlatformHandle handle(
command_line.GetSwitchValueNative(kMojoNamedPlatformChannelPipeSwitch));
if (!handle.is_valid())
return ScopedInternalPlatformHandle();
return CreateClientHandle(handle);
}
void NamedPlatformChannelPair::PrepareToPassClientHandleToChildProcess(
base::CommandLine* command_line) const {
DCHECK(command_line);
// 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(kMojoNamedPlatformChannelPipeSwitch))
<< "Child command line already has switch --"
<< kMojoNamedPlatformChannelPipeSwitch << "="
<< command_line->GetSwitchValueNative(
kMojoNamedPlatformChannelPipeSwitch);
// (Any existing switch won't actually be removed from the command line, but
// the last one appended takes precedence.)
command_line->AppendSwitchNative(kMojoNamedPlatformChannelPipeSwitch,
pipe_handle_.name);
}
} // namespace edk
} // namespace mojo
// Copyright 2016 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.
#ifndef MOJO_EDK_EMBEDDER_NAMED_PLATFORM_CHANNEL_PAIR_H_
#define MOJO_EDK_EMBEDDER_NAMED_PLATFORM_CHANNEL_PAIR_H_
#include <string>
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/strings/string16.h"
#include "build/build_config.h"
#include "mojo/edk/embedder/named_platform_handle.h"
#include "mojo/edk/embedder/scoped_platform_handle.h"
#include "mojo/edk/system/system_impl_export.h"
namespace base {
class CommandLine;
}
namespace mojo {
namespace edk {
// This is used to create a named bidirectional pipe to connect new child
// processes. The resulting server handle should be passed to the EDK, and the
// child end passed as a pipe name on the command line to the child process. The
// child process can then retrieve the pipe name from the command line and
// resolve it into a client handle.
class MOJO_SYSTEM_IMPL_EXPORT NamedPlatformChannelPair {
public:
struct Options {
#if defined(OS_WIN)
// If non-empty, a security descriptor to use when creating the pipe. If
// empty, a default security descriptor will be used. See
// kDefaultSecurityDescriptor in named_platform_handle_utils_win.cc.
base::string16 security_descriptor;
#else
// On POSIX, every new NamedPlatformChannelPair creates a new server socket
// with a random name. This controls the directory where that happens.
base::FilePath socket_dir;
#endif
};
NamedPlatformChannelPair(const Options& options = {});
~NamedPlatformChannelPair();
// Note: It is NOT acceptable to use this handle as a generic pipe channel. It
// MUST be passed to OutgoingBrokerClientInvitation::Send() only.
ScopedInternalPlatformHandle PassServerHandle();
// To be called in the child process, after the parent process called
// |PrepareToPassClientHandleToChildProcess()| and launched the child (using
// the provided data), to create a client handle connected to the server
// handle (in the parent process).
static ScopedInternalPlatformHandle PassClientHandleFromParentProcess(
const base::CommandLine& command_line);
// Prepares to pass the client channel to a new child process, to be launched
// using |LaunchProcess()| (from base/launch.h). Modifies |*command_line| and
// |*handle_passing_info| as needed.
// Note: For Windows, this method only works on Vista and later.
void PrepareToPassClientHandleToChildProcess(
base::CommandLine* command_line) const;
const NamedPlatformHandle& handle() const { return pipe_handle_; }
private:
NamedPlatformHandle pipe_handle_;
ScopedInternalPlatformHandle server_handle_;
DISALLOW_COPY_AND_ASSIGN(NamedPlatformChannelPair);
};
} // namespace edk
} // namespace mojo
#endif // MOJO_EDK_EMBEDDER_NAMED_PLATFORM_CHANNEL_PAIR_H_
// Copyright 2016 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.
#ifndef MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_H_
#define MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_H_
#include "build/build_config.h"
#if defined(OS_WIN)
#include "mojo/edk/embedder/named_platform_handle_win.h"
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
#include "mojo/edk/embedder/named_platform_handle_posix.h"
#endif
#endif // MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_H_
// Copyright 2018 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.
#ifndef MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_POSIX_H_
#define MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_POSIX_H_
#include <string>
#include "base/strings/string_piece.h"
#include "mojo/edk/system/system_impl_export.h"
namespace mojo {
namespace edk {
struct MOJO_SYSTEM_IMPL_EXPORT NamedPlatformHandle {
NamedPlatformHandle() {}
explicit NamedPlatformHandle(const base::StringPiece& name)
: name(name.as_string()) {}
bool is_valid() const { return !name.empty(); }
std::string name;
};
} // namespace edk
} // namespace mojo
#endif // MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_POSIX_H_
// Copyright 2016 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.
#ifndef MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_UTILS_H_
#define MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_UTILS_H_
#include "build/build_config.h"
#include "mojo/edk/embedder/scoped_platform_handle.h"
#include "mojo/edk/system/system_impl_export.h"
#if defined(OS_WIN)
#include "base/strings/string16.h"
#endif
namespace mojo {
namespace edk {
struct NamedPlatformHandle;
#if defined(OS_POSIX)
// The maximum length of the name of a unix domain socket. The standard size on
// linux is 108, mac is 104. To maintain consistency across platforms we
// standardize on the smaller value.
const size_t kMaxSocketNameLength = 104;
#endif
struct CreateServerHandleOptions {
#if defined(OS_WIN)
// If true, creating a server handle will fail if another pipe with the same
// name exists.
bool enforce_uniqueness = true;
// If non-empty, a security descriptor to use when creating the pipe. If
// empty, a default security descriptor will be used. See
// kDefaultSecurityDescriptor in named_platform_handle_utils_win.cc.
base::string16 security_descriptor;
#endif
};
// Creates a client platform handle from |handle|. This may block until |handle|
// is ready to receive connections.
MOJO_SYSTEM_IMPL_EXPORT ScopedInternalPlatformHandle
CreateClientHandle(const NamedPlatformHandle& handle);
// Creates a server platform handle from |handle|.
MOJO_SYSTEM_IMPL_EXPORT ScopedInternalPlatformHandle
CreateServerHandle(const NamedPlatformHandle& handle,
const CreateServerHandleOptions& options = {});
} // namespace edk
} // namespace mojo
#endif // MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_UTILS_H_
// Copyright 2017 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/edk/embedder/named_platform_handle_utils.h"
#include "mojo/edk/embedder/named_platform_handle.h"
namespace mojo {
namespace edk {
ScopedInternalPlatformHandle CreateClientHandle(
const NamedPlatformHandle& named_handle) {
// TODO(fuchsia): Implement, or remove dependencies (crbug.com/754038).
NOTREACHED();
return ScopedInternalPlatformHandle();
}
ScopedInternalPlatformHandle CreateServerHandle(
const NamedPlatformHandle& named_handle,
const CreateServerHandleOptions& options) {
// TODO(fuchsia): Implement, or remove dependencies (crbug.com/754038).
NOTREACHED();
return ScopedInternalPlatformHandle();
}
} // namespace edk
} // namespace mojo
// Copyright 2016 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/edk/embedder/named_platform_handle_utils.h"
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "mojo/edk/embedder/named_platform_handle.h"
namespace mojo {
namespace edk {
namespace {
// This function fills in |unix_addr| with the appropriate data for the socket,
// and sets |unix_addr_len| to the length of the data therein.
// Returns true on success, or false on failure (typically because |handle.name|
// violated the naming rules).
bool MakeUnixAddr(const NamedPlatformHandle& handle,
struct sockaddr_un* unix_addr,
size_t* unix_addr_len) {
DCHECK(unix_addr);
DCHECK(unix_addr_len);
DCHECK(handle.is_valid());
// We reject handle.name.length() == kMaxSocketNameLength to make room for the
// NUL terminator at the end of the string.
if (handle.name.length() >= kMaxSocketNameLength) {
LOG(ERROR) << "Socket name too long: " << handle.name;
return false;
}
// Create unix_addr structure.
memset(unix_addr, 0, sizeof(struct sockaddr_un));
unix_addr->sun_family = AF_UNIX;
strncpy(unix_addr->sun_path, handle.name.c_str(), kMaxSocketNameLength);
*unix_addr_len =
offsetof(struct sockaddr_un, sun_path) + handle.name.length();
return true;
}
// This function creates a unix domain socket, and set it as non-blocking.
// If successful, this returns a ScopedInternalPlatformHandle containing the
// socket. Otherwise, this returns an invalid ScopedInternalPlatformHandle.
ScopedInternalPlatformHandle CreateUnixDomainSocket(bool needs_connection) {
// Create the unix domain socket.
InternalPlatformHandle socket_handle(socket(AF_UNIX, SOCK_STREAM, 0));
socket_handle.needs_connection = needs_connection;
ScopedInternalPlatformHandle handle(socket_handle);
if (!handle.is_valid()) {
PLOG(ERROR) << "Failed to create AF_UNIX socket.";
return ScopedInternalPlatformHandle();
}
// Now set it as non-blocking.
if (!base::SetNonBlocking(handle.get().handle)) {
PLOG(ERROR) << "base::SetNonBlocking() failed " << handle.get().handle;
return ScopedInternalPlatformHandle();
}
return handle;
}
} // namespace
ScopedInternalPlatformHandle CreateClientHandle(
const NamedPlatformHandle& named_handle) {
if (!named_handle.is_valid())
return ScopedInternalPlatformHandle();
struct sockaddr_un unix_addr;
size_t unix_addr_len;
if (!MakeUnixAddr(named_handle, &unix_addr, &unix_addr_len))
return ScopedInternalPlatformHandle();
ScopedInternalPlatformHandle handle = CreateUnixDomainSocket(false);
if (!handle.is_valid())
return ScopedInternalPlatformHandle();
if (HANDLE_EINTR(connect(handle.get().handle,
reinterpret_cast<sockaddr*>(&unix_addr),
unix_addr_len)) < 0) {
PLOG(ERROR) << "connect " << named_handle.name;
return ScopedInternalPlatformHandle();
}
return handle;
}
ScopedInternalPlatformHandle CreateServerHandle(
const NamedPlatformHandle& named_handle,
const CreateServerHandleOptions& options) {
if (!named_handle.is_valid())
return ScopedInternalPlatformHandle();
// Make sure the path we need exists.
base::FilePath socket_dir = base::FilePath(named_handle.name).DirName();
if (!base::CreateDirectory(socket_dir)) {
LOG(ERROR) << "Couldn't create directory: " << socket_dir.value();
return ScopedInternalPlatformHandle();
}
// Delete any old FS instances.
if (unlink(named_handle.name.c_str()) < 0 && errno != ENOENT) {
PLOG(ERROR) << "unlink " << named_handle.name;
return ScopedInternalPlatformHandle();
}
struct sockaddr_un unix_addr;
size_t unix_addr_len;
if (!MakeUnixAddr(named_handle, &unix_addr, &unix_addr_len))
return ScopedInternalPlatformHandle();
ScopedInternalPlatformHandle handle = CreateUnixDomainSocket(true);
if (!handle.is_valid())
return ScopedInternalPlatformHandle();
// Bind the socket.
if (bind(handle.get().handle, reinterpret_cast<const sockaddr*>(&unix_addr),
unix_addr_len) < 0) {
PLOG(ERROR) << "bind " << named_handle.name;
return ScopedInternalPlatformHandle();
}
// Start listening on the socket.
if (listen(handle.get().handle, SOMAXCONN) < 0) {
PLOG(ERROR) << "listen " << named_handle.name;
unlink(named_handle.name.c_str());
return ScopedInternalPlatformHandle();
}
return handle;
}
} // namespace edk
} // namespace mojo
// Copyright 2016 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/edk/embedder/named_platform_handle_utils.h"
#include <sddl.h>
#include <windows.h>
#include <memory>
#include "base/logging.h"
#include "base/win/windows_version.h"
#include "mojo/edk/embedder/named_platform_handle.h"
namespace mojo {
namespace edk {
namespace {
// A DACL to grant:
// GA = Generic All
// access to:
// SY = LOCAL_SYSTEM
// BA = BUILTIN_ADMINISTRATORS
// OW = OWNER_RIGHTS
constexpr base::char16 kDefaultSecurityDescriptor[] =
L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;OW)";
} // namespace
ScopedInternalPlatformHandle CreateClientHandle(
const NamedPlatformHandle& named_handle) {
if (!named_handle.is_valid())
return ScopedInternalPlatformHandle();
base::string16 pipe_name = named_handle.pipe_name();
// Note: This may block.
if (!WaitNamedPipeW(pipe_name.c_str(), NMPWAIT_USE_DEFAULT_WAIT))
return ScopedInternalPlatformHandle();
const DWORD kDesiredAccess = GENERIC_READ | GENERIC_WRITE;
// The SECURITY_ANONYMOUS flag means that the server side cannot impersonate
// the client.
const DWORD kFlags =
SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS | FILE_FLAG_OVERLAPPED;
ScopedInternalPlatformHandle handle(
InternalPlatformHandle(CreateFileW(pipe_name.c_str(), kDesiredAccess,
0, // No sharing.
nullptr, OPEN_EXISTING, kFlags,
nullptr))); // No template file.
// The server may have stopped accepting a connection between the
// WaitNamedPipe() and CreateFile(). If this occurs, an invalid handle is
// returned.
DPLOG_IF(ERROR, !handle.is_valid())
<< "Named pipe " << named_handle.pipe_name()
<< " could not be opened after WaitNamedPipe succeeded";
return handle;
}
ScopedInternalPlatformHandle CreateServerHandle(
const NamedPlatformHandle& named_handle,
const CreateServerHandleOptions& options) {
if (!named_handle.is_valid())
return ScopedInternalPlatformHandle();
PSECURITY_DESCRIPTOR security_desc = nullptr;
ULONG security_desc_len = 0;
PCHECK(ConvertStringSecurityDescriptorToSecurityDescriptor(
options.security_descriptor.empty() ? kDefaultSecurityDescriptor
: options.security_descriptor.c_str(),
SDDL_REVISION_1, &security_desc, &security_desc_len));
std::unique_ptr<void, decltype(::LocalFree)*> p(security_desc, ::LocalFree);
SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES),
security_desc, FALSE};
const DWORD kOpenMode = options.enforce_uniqueness
? PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
FILE_FLAG_FIRST_PIPE_INSTANCE
: PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED;
const DWORD kPipeMode =
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_REJECT_REMOTE_CLIENTS;
InternalPlatformHandle handle(
CreateNamedPipeW(named_handle.pipe_name().c_str(), kOpenMode, kPipeMode,
options.enforce_uniqueness ? 1 : 255, // Max instances.
4096, // Out buffer size.
4096, // In buffer size.
5000, // Timeout in milliseconds.
&security_attributes));
handle.needs_connection = true;
return ScopedInternalPlatformHandle(handle);
}
} // namespace edk
} // namespace mojo
// Copyright 2018 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.
#ifndef MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_WIN_H_
#define MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_WIN_H_
#include "base/strings/string16.h"
#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
#include "mojo/edk/system/system_impl_export.h"
namespace mojo {
namespace edk {
struct MOJO_SYSTEM_IMPL_EXPORT NamedPlatformHandle {
NamedPlatformHandle() {}
explicit NamedPlatformHandle(const base::StringPiece& name)
: name(base::UTF8ToUTF16(name)) {}
explicit NamedPlatformHandle(const base::StringPiece16& name)
: name(name.as_string()) {}
bool is_valid() const { return !name.empty(); }
base::string16 pipe_name() const { return L"\\\\.\\pipe\\mojo." + name; }
base::string16 name;
};
} // namespace edk
} // namespace mojo
#endif // MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_WIN_H_
// Copyright 2014 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/edk/embedder/platform_channel_pair.h"
#include <utility>
#include "base/logging.h"
#include "build/build_config.h"
namespace mojo {
namespace edk {
const char PlatformChannelPair::kMojoPlatformChannelHandleSwitch[] =
"mojo-platform-channel-handle";
PlatformChannelPair::~PlatformChannelPair() {
}
ScopedInternalPlatformHandle PlatformChannelPair::PassServerHandle() {
return std::move(server_handle_);
}
ScopedInternalPlatformHandle PlatformChannelPair::PassClientHandle() {
return std::move(client_handle_);
}
void PlatformChannelPair::ChildProcessLaunched() {
DCHECK(client_handle_.is_valid());
#if defined(OS_FUCHSIA)
// The |client_handle_| is transferred, not cloned, to the child.
ignore_result(client_handle_.release());
#else
client_handle_.reset();
#endif
}
} // namespace edk
} // namespace mojo
// Copyright 2014 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.
#ifndef MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_PAIR_H_
#define MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_PAIR_H_
#include <memory>
#include "base/macros.h"
#include "base/process/launch.h"
#include "build/build_config.h"
#include "mojo/edk/embedder/scoped_platform_handle.h"
#include "mojo/edk/system/system_impl_export.h"
namespace base {
class CommandLine;
}
namespace mojo {
namespace edk {
// It would be nice to refactor base/process/launch.h to have a more platform-
// independent way of representing handles that are passed to child processes.
#if defined(OS_WIN)
using HandlePassingInformation = base::HandlesToInheritVector;
#elif defined(OS_FUCHSIA)
using HandlePassingInformation = base::HandlesToTransferVector;
#elif defined(OS_POSIX)
using HandlePassingInformation = base::FileHandleMappingVector;
#else
#error "Unsupported."
#endif
// This is used to create a pair of |InternalPlatformHandle|s that are connected
// by a suitable (platform-specific) bidirectional "pipe" (e.g., socket on
// POSIX, named pipe on Windows). The resulting handles can then be used in the
// same process (e.g., in tests) or between processes. (The "server" handle is
// the one that will be used in the process that created the pair, whereas the
// "client" handle is the one that will be used in a different process.)
//
// This class provides facilities for passing the client handle to a child
// process. The parent should call |PrepareToPassClientHandlelToChildProcess()|
// to get the data needed to do this, spawn the child using that data, and then
// call |ChildProcessLaunched()|. Note that on Windows this facility (will) only
// work on Vista and later (TODO(vtl)).
//
// Note: |PlatformChannelPair()|, |PassClientHandleFromParentProcess()| and
// |PrepareToPassClientHandleToChildProcess()| have platform-specific
// implementations.
//
// Note: On POSIX platforms, to write to the "pipe", use
// |PlatformChannel{Write,Writev}()| (from platform_channel_utils_posix.h)
// instead of |write()|, |writev()|, etc. Otherwise, you have to worry about
// platform differences in suppressing |SIGPIPE|.
class MOJO_SYSTEM_IMPL_EXPORT PlatformChannelPair {
public:
static const char kMojoPlatformChannelHandleSwitch[];
// If |client_is_blocking| is true, then the client handle only supports
// blocking reads and writes. The default is nonblocking.
PlatformChannelPair(bool client_is_blocking = false);
~PlatformChannelPair();
ScopedInternalPlatformHandle PassServerHandle();
// For in-process use (e.g., in tests or to pass over another channel).
ScopedInternalPlatformHandle PassClientHandle();
// To be called in the child process, after the parent process called
// |PrepareToPassClientHandleToChildProcess()| and launched the child (using
// the provided data), to create a client handle connected to the server
// handle (in the parent process).
// TODO(jcivelli): remove the command_line param. http://crbug.com/670106
static ScopedInternalPlatformHandle PassClientHandleFromParentProcess(
const base::CommandLine& command_line);
// Like above, but gets the handle from the passed in string.
static ScopedInternalPlatformHandle
PassClientHandleFromParentProcessFromString(const std::string& value);
// Prepares to pass the client channel to a new child process, to be launched
// using |LaunchProcess()| (from base/launch.h). Modifies |*command_line| and
// |*handle_passing_info| as needed.
// Note: For Windows, this method only works on Vista and later.
void PrepareToPassClientHandleToChildProcess(
base::CommandLine* command_line,
HandlePassingInformation* handle_passing_info) const;
// Like above, but returns a string instead of changing the command line.
std::string PrepareToPassClientHandleToChildProcessAsString(
HandlePassingInformation* handle_passing_info) const;
#if defined(OS_FUCHSIA)
// Like above, but accepts a caller-supplied client |handle|.
// TODO(wez): Consider incorporating this call into other platform
// implementations.
static void PrepareToPassHandleToChildProcess(
const InternalPlatformHandle& handle,
base::CommandLine* command_line,
HandlePassingInformation* handle_passing_info);
#endif
// To be called once the child process has been successfully launched, to do
// any cleanup necessary.
void ChildProcessLaunched();
private:
ScopedInternalPlatformHandle server_handle_;
ScopedInternalPlatformHandle client_handle_;
DISALLOW_COPY_AND_ASSIGN(PlatformChannelPair);
};
} // namespace edk
} // namespace mojo
#endif // MOJO_EDK_EMBEDDER_PLATFORM_CHANNEL_PAIR_H_
// Copyright 2014 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/edk/embedder/platform_channel_pair.h"
#include <zircon/process.h>
#include <zircon/processargs.h>
#include <zircon/syscalls.h>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/string_number_conversions.h"
#include "mojo/edk/embedder/platform_handle.h"
namespace mojo {
namespace edk {
namespace {
std::string PrepareToPassHandleToChildProcessAsString(
const InternalPlatformHandle& handle,
HandlePassingInformation* handle_passing_info) {
DCHECK(handle.is_valid());
const uint32_t id = PA_HND(PA_USER0, 0);
handle_passing_info->push_back({id, handle.as_handle()});
return base::UintToString(id);
}
} // namespace
PlatformChannelPair::PlatformChannelPair(bool client_is_blocking) {
zx_handle_t handles[2] = {};
zx_status_t result = zx_channel_create(0, &handles[0], &handles[1]);
CHECK_EQ(ZX_OK, result);
server_handle_.reset(InternalPlatformHandle::ForHandle(handles[0]));
DCHECK(server_handle_.is_valid());
client_handle_.reset(InternalPlatformHandle::ForHandle(handles[1]));
DCHECK(client_handle_.is_valid());
}
// static
ScopedInternalPlatformHandle
PlatformChannelPair::PassClientHandleFromParentProcess(
const base::CommandLine& command_line) {
std::string handle_string =
command_line.GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch);
return PassClientHandleFromParentProcessFromString(handle_string);
}
ScopedInternalPlatformHandle
PlatformChannelPair::PassClientHandleFromParentProcessFromString(
const std::string& value) {
unsigned int id = 0;
if (value.empty() || !base::StringToUint(value, &id)) {
LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch;
return ScopedInternalPlatformHandle();
}
return ScopedInternalPlatformHandle(InternalPlatformHandle::ForHandle(
zx_take_startup_handle(base::checked_cast<uint32_t>(id))));
}
void PlatformChannelPair::PrepareToPassClientHandleToChildProcess(
base::CommandLine* command_line,
HandlePassingInformation* handle_passing_info) const {
return PrepareToPassHandleToChildProcess(client_handle_.get(), command_line,
handle_passing_info);
}
// static
void PlatformChannelPair::PrepareToPassHandleToChildProcess(
const InternalPlatformHandle& handle,
base::CommandLine* command_line,
HandlePassingInformation* handle_passing_info) {
DCHECK(command_line);
// 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(kMojoPlatformChannelHandleSwitch))
<< "Child command line already has switch --"
<< kMojoPlatformChannelHandleSwitch << "="
<< command_line->GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch);
// (Any existing switch won't actually be removed from the command line, but
// the last one appended takes precedence.)
command_line->AppendSwitchASCII(
kMojoPlatformChannelHandleSwitch,
PrepareToPassHandleToChildProcessAsString(handle, handle_passing_info));
}
} // namespace edk
} // namespace mojo
// Copyright 2014 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/edk/embedder/platform_channel_pair.h"
#include <fcntl.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#include <limits>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/posix/global_descriptors.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "build/build_config.h"
#include "mojo/edk/embedder/platform_handle.h"
#if !defined(OS_NACL_SFI)
#include <sys/socket.h>
#else
#include "native_client/src/public/imc_syscalls.h"
#endif
#if !defined(SO_PEEK_OFF)
#define SO_PEEK_OFF 42
#endif
namespace mojo {
namespace edk {
namespace {
#if defined(OS_ANDROID)
enum {
// Leave room for any other descriptors defined in content for example.
// TODO(jcivelli): consider changing base::GlobalDescriptors to generate a
// key when setting the file descriptor (http://crbug.com/676442).
kAndroidClientHandleDescriptor =
base::GlobalDescriptors::kBaseDescriptor + 10000,
};
#else
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;
}
#endif
} // namespace
PlatformChannelPair::PlatformChannelPair(bool client_is_blocking) {
// Create the Unix domain socket.
int fds[2];
// TODO(vtl): Maybe fail gracefully if |socketpair()| fails.
#if defined(OS_NACL_SFI)
PCHECK(imc_socketpair(fds) == 0);
#else
PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0);
// Set the ends to nonblocking.
PCHECK(fcntl(fds[0], F_SETFL, O_NONBLOCK) == 0);
if (!client_is_blocking)
PCHECK(fcntl(fds[1], F_SETFL, O_NONBLOCK) == 0);
#if defined(OS_MACOSX)
// This turns off |SIGPIPE| when writing to a closed socket (causing it to
// fail with |EPIPE| instead). On Linux, we have to use |send...()| with
// |MSG_NOSIGNAL| -- which is not supported on Mac -- instead.
int no_sigpipe = 1;
PCHECK(setsockopt(fds[0], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe,
sizeof(no_sigpipe)) == 0);
PCHECK(setsockopt(fds[1], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe,
sizeof(no_sigpipe)) == 0);
#endif // defined(OS_MACOSX)
#endif // defined(OS_NACL_SFI)
server_handle_.reset(InternalPlatformHandle(fds[0]));
DCHECK(server_handle_.is_valid());
client_handle_.reset(InternalPlatformHandle(fds[1]));
DCHECK(client_handle_.is_valid());
}
// static
ScopedInternalPlatformHandle
PlatformChannelPair::PassClientHandleFromParentProcess(
const base::CommandLine& command_line) {
std::string client_fd_string =
command_line.GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch);
return PassClientHandleFromParentProcessFromString(client_fd_string);
}
ScopedInternalPlatformHandle
PlatformChannelPair::PassClientHandleFromParentProcessFromString(
const std::string& value) {
int client_fd = -1;
#if defined(OS_ANDROID)
base::GlobalDescriptors::Key key = -1;
if (value.empty() || !base::StringToUint(value, &key)) {
LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch;
return ScopedInternalPlatformHandle();
}
client_fd = base::GlobalDescriptors::GetInstance()->Get(key);
#else
if (value.empty() ||
!base::StringToInt(value, &client_fd) ||
client_fd < base::GlobalDescriptors::kBaseDescriptor) {
LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch;
return ScopedInternalPlatformHandle();
}
#endif
return ScopedInternalPlatformHandle(InternalPlatformHandle(client_fd));
}
void PlatformChannelPair::PrepareToPassClientHandleToChildProcess(
base::CommandLine* command_line,
base::FileHandleMappingVector* handle_passing_info) const {
DCHECK(command_line);
// 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(kMojoPlatformChannelHandleSwitch))
<< "Child command line already has switch --"
<< kMojoPlatformChannelHandleSwitch << "="
<< command_line->GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch);
// (Any existing switch won't actually be removed from the command line, but
// the last one appended takes precedence.)
command_line->AppendSwitchASCII(
kMojoPlatformChannelHandleSwitch,
PrepareToPassClientHandleToChildProcessAsString(handle_passing_info));
}
std::string
PlatformChannelPair::PrepareToPassClientHandleToChildProcessAsString(
HandlePassingInformation* handle_passing_info) const {
#if defined(OS_ANDROID)
int fd = client_handle_.get().handle;
handle_passing_info->push_back(
std::pair<int, int>(fd, kAndroidClientHandleDescriptor));
return base::UintToString(kAndroidClientHandleDescriptor);
#else
DCHECK(handle_passing_info);
// This is an arbitrary sanity check. (Note that this guarantees that the loop
// below will terminate sanely.)
CHECK_LT(handle_passing_info->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 |*handle_passing_info|,
// but |*handle_passing_info| should be very small (usually/often empty).
int target_fd = base::GlobalDescriptors::kBaseDescriptor;
while (IsTargetDescriptorUsed(*handle_passing_info, target_fd))
target_fd++;
handle_passing_info->push_back(
std::pair<int, int>(client_handle_.get().handle, target_fd));
return base::IntToString(target_fd);
#endif
}
} // namespace edk
} // namespace mojo
// Copyright 2014 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/edk/embedder/platform_channel_pair.h"
#include <windows.h>
#include <string>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "mojo/edk/embedder/platform_handle.h"
namespace mojo {
namespace edk {
namespace {
std::wstring GeneratePipeName() {
return base::StringPrintf(L"\\\\.\\pipe\\mojo.%u.%u.%I64u",
GetCurrentProcessId(), GetCurrentThreadId(),
base::RandUint64());
}
} // namespace
PlatformChannelPair::PlatformChannelPair(bool client_is_blocking) {
std::wstring pipe_name = GeneratePipeName();
DWORD kOpenMode =
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE;
const DWORD kPipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE;
server_handle_.reset(InternalPlatformHandle(
CreateNamedPipeW(pipe_name.c_str(), kOpenMode, kPipeMode,
1, // Max instances.
4096, // Out buffer size.
4096, // In buffer size.
5000, // Timeout in milliseconds.
nullptr))); // Default security descriptor.
PCHECK(server_handle_.is_valid());
const DWORD kDesiredAccess = GENERIC_READ | GENERIC_WRITE;
// The SECURITY_ANONYMOUS flag means that the server side cannot impersonate
// the client.
DWORD kFlags = SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS;
if (!client_is_blocking)
kFlags |= FILE_FLAG_OVERLAPPED;
// Allow the handle to be inherited by child processes.
SECURITY_ATTRIBUTES security_attributes = {
sizeof(SECURITY_ATTRIBUTES), nullptr, TRUE};
client_handle_.reset(InternalPlatformHandle(
CreateFileW(pipe_name.c_str(), kDesiredAccess,
0, // No sharing.
&security_attributes, OPEN_EXISTING, kFlags,
nullptr))); // No template file.
PCHECK(client_handle_.is_valid());
// Since a client has connected, ConnectNamedPipe() should return zero and
// GetLastError() should return ERROR_PIPE_CONNECTED.
CHECK(!ConnectNamedPipe(server_handle_.get().handle, nullptr));
PCHECK(GetLastError() == ERROR_PIPE_CONNECTED);
}
// static
ScopedInternalPlatformHandle
PlatformChannelPair::PassClientHandleFromParentProcess(
const base::CommandLine& command_line) {
std::string client_handle_string =
command_line.GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch);
return PassClientHandleFromParentProcessFromString(client_handle_string);
}
ScopedInternalPlatformHandle
PlatformChannelPair::PassClientHandleFromParentProcessFromString(
const std::string& value) {
int client_handle_value = 0;
if (value.empty() ||
!base::StringToInt(value, &client_handle_value)) {
LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch;
return ScopedInternalPlatformHandle();
}
return ScopedInternalPlatformHandle(
InternalPlatformHandle(LongToHandle(client_handle_value)));
}
void PlatformChannelPair::PrepareToPassClientHandleToChildProcess(
base::CommandLine* command_line,
base::HandlesToInheritVector* handle_passing_info) const {
DCHECK(command_line);
// 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(kMojoPlatformChannelHandleSwitch))
<< "Child command line already has switch --"
<< kMojoPlatformChannelHandleSwitch << "="
<< command_line->GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch);
// (Any existing switch won't actually be removed from the command line, but
// the last one appended takes precedence.)
command_line->AppendSwitchASCII(
kMojoPlatformChannelHandleSwitch,
PrepareToPassClientHandleToChildProcessAsString(handle_passing_info));
}
std::string
PlatformChannelPair::PrepareToPassClientHandleToChildProcessAsString(
HandlePassingInformation* handle_passing_info) const {
DCHECK(handle_passing_info);
DCHECK(client_handle_.is_valid());
handle_passing_info->push_back(client_handle_.get().handle);
return base::IntToString(HandleToLong(client_handle_.get().handle));
}
} // namespace edk
} // namespace mojo
......@@ -50,10 +50,6 @@ source_set("test_sources") {
"trap_unittest.cc",
]
if (is_posix) {
sources += [ "platform_channel_pair_posix_unittest.cc" ]
}
if (!is_ios) {
sources += [
"data_pipe_unittest.cc",
......
......@@ -11,8 +11,6 @@
#include "base/memory/ref_counted.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "mojo/edk/embedder/named_platform_channel_pair.h"
#include "mojo/edk/embedder/named_platform_handle.h"
#include "mojo/edk/embedder/scoped_platform_handle.h"
#include "mojo/edk/system/broker_messages.h"
#include "mojo/edk/system/platform_handle_utils.h"
......
......@@ -11,14 +11,13 @@
#include "base/memory/platform_shared_memory_region.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_piece.h"
#include "mojo/edk/embedder/named_platform_handle.h"
#include "mojo/edk/embedder/named_platform_handle_utils.h"
#include "mojo/edk/embedder/platform_handle.h"
#include "mojo/edk/embedder/scoped_platform_handle.h"
#include "mojo/edk/system/broker.h"
#include "mojo/edk/system/broker_messages.h"
#include "mojo/edk/system/channel.h"
#include "mojo/edk/system/platform_handle_utils.h"
#include "mojo/public/cpp/platform/named_platform_channel.h"
namespace mojo {
namespace edk {
......@@ -110,8 +109,11 @@ Broker::Broker(ScopedInternalPlatformHandle handle)
const base::char16* name_data =
reinterpret_cast<const base::char16*>(data + 1);
CHECK(data->pipe_name_length);
inviter_channel_ = CreateClientHandle(NamedPlatformHandle(
base::StringPiece16(name_data, data->pipe_name_length)));
inviter_channel_ = PlatformHandleToScopedInternalPlatformHandle(
NamedPlatformChannel::ConnectToServer(
base::StringPiece16(name_data, data->pipe_name_length).as_string())
.TakePlatformHandle());
inviter_channel_.get().needs_connection = true;
}
}
......
......@@ -8,7 +8,8 @@
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/threading/thread.h"
#include "mojo/edk/embedder/platform_channel_pair.h"
#include "mojo/edk/system/platform_handle_utils.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -239,7 +240,7 @@ class ChannelTestShutdownAndWriteDelegate : public Channel::Delegate {
TEST(ChannelTest, PeerShutdownDuringRead) {
base::MessageLoop message_loop(base::MessageLoop::TYPE_IO);
PlatformChannelPair channel_pair;
PlatformChannel channel;
// Create a "client" Channel with one end of the pipe, and Start() it.
std::unique_ptr<base::Thread> client_thread =
......@@ -248,7 +249,9 @@ TEST(ChannelTest, PeerShutdownDuringRead) {
base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
scoped_refptr<Channel> client_channel = Channel::Create(
nullptr, ConnectionParams(channel_pair.PassClientHandle()),
nullptr,
ConnectionParams(PlatformHandleToScopedInternalPlatformHandle(
channel.TakeRemoteEndpoint().TakePlatformHandle())),
client_thread->task_runner());
client_channel->Start();
......@@ -264,9 +267,10 @@ TEST(ChannelTest, PeerShutdownDuringRead) {
// is received.
base::RunLoop run_loop;
ChannelTestShutdownAndWriteDelegate server_delegate(
channel_pair.PassServerHandle(), message_loop.task_runner(),
std::move(client_channel), std::move(client_thread),
run_loop.QuitClosure());
PlatformHandleToScopedInternalPlatformHandle(
channel.TakeLocalEndpoint().TakePlatformHandle()),
message_loop.task_runner(), std::move(client_channel),
std::move(client_thread), run_loop.QuitClosure());
run_loop.Run();
}
......
......@@ -14,7 +14,6 @@
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "mojo/edk/embedder/embedder.h"
#include "mojo/edk/embedder/platform_channel_pair.h"
#include "mojo/edk/system/test_utils.h"
#include "mojo/edk/test/mojo_test_base.h"
#include "mojo/public/c/system/data_pipe.h"
......
......@@ -22,7 +22,6 @@
#include "base/run_loop.h"
#include "base/strings/string_split.h"
#include "build/build_config.h"
#include "mojo/edk/embedder/platform_channel_pair.h"
#include "mojo/edk/embedder/scoped_platform_handle.h"
#include "mojo/edk/system/handle_signals_state.h"
#include "mojo/edk/system/test_utils.h"
......
......@@ -19,15 +19,15 @@
#include "base/rand_util.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "mojo/edk/embedder/named_platform_channel_pair.h"
#include "mojo/edk/embedder/named_platform_handle.h"
#include "mojo/edk/embedder/platform_channel_pair.h"
#include "mojo/edk/system/broker.h"
#include "mojo/edk/system/broker_host.h"
#include "mojo/edk/system/configuration.h"
#include "mojo/edk/system/core.h"
#include "mojo/edk/system/platform_handle_utils.h"
#include "mojo/edk/system/request_context.h"
#include "mojo/edk/system/user_message_impl.h"
#include "mojo/public/cpp/platform/named_platform_channel.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#if defined(OS_MACOSX) && !defined(OS_IOS)
#include "mojo/edk/system/mach_port_relay.h"
......@@ -319,21 +319,28 @@ void NodeController::SendBrokerClientInvitationOnIOThread(
DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
#if !defined(OS_MACOSX) && !defined(OS_NACL) && !defined(OS_FUCHSIA)
PlatformChannelPair node_channel;
ScopedInternalPlatformHandle server_handle = node_channel.PassServerHandle();
PlatformChannel node_channel;
ScopedInternalPlatformHandle server_handle =
PlatformHandleToScopedInternalPlatformHandle(
node_channel.TakeLocalEndpoint().TakePlatformHandle());
// BrokerHost owns itself.
BrokerHost* broker_host = new BrokerHost(
target_process.get(), connection_params.TakeChannelHandle(),
process_error_callback);
bool channel_ok = broker_host->SendChannel(node_channel.PassClientHandle());
bool channel_ok =
broker_host->SendChannel(PlatformHandleToScopedInternalPlatformHandle(
node_channel.TakeRemoteEndpoint().TakePlatformHandle()));
#if defined(OS_WIN)
if (!channel_ok) {
// On Windows the above operation may fail if the channel is crossing a
// session boundary. In that case we fall back to a named pipe.
NamedPlatformChannelPair named_channel;
server_handle = named_channel.PassServerHandle();
broker_host->SendNamedChannel(named_channel.handle().name);
NamedPlatformChannel::Options options;
NamedPlatformChannel named_channel(options);
server_handle = PlatformHandleToScopedInternalPlatformHandle(
named_channel.TakeServerEndpoint().TakePlatformHandle());
server_handle.get().needs_connection = true;
broker_host->SendNamedChannel(named_channel.GetServerName());
}
#else
CHECK(channel_ok);
......@@ -822,8 +829,10 @@ void NodeController::OnAddBrokerClient(const ports::NodeName& from_node,
return;
}
PlatformChannelPair broker_channel;
ConnectionParams connection_params(broker_channel.PassServerHandle());
PlatformChannel broker_channel;
ConnectionParams connection_params(
PlatformHandleToScopedInternalPlatformHandle(
broker_channel.TakeLocalEndpoint().TakePlatformHandle()));
scoped_refptr<NodeChannel> client =
NodeChannel::Create(this, std::move(connection_params), io_task_runner_,
ProcessErrorCallback());
......@@ -843,7 +852,10 @@ void NodeController::OnAddBrokerClient(const ports::NodeName& from_node,
DVLOG(1) << "Broker " << name_ << " accepting client " << client_name
<< " from peer " << from_node;
sender->BrokerClientAdded(client_name, broker_channel.PassClientHandle());
sender->BrokerClientAdded(
client_name,
PlatformHandleToScopedInternalPlatformHandle(
broker_channel.TakeRemoteEndpoint().TakePlatformHandle()));
}
void NodeController::OnBrokerClientAdded(
......@@ -1025,9 +1037,13 @@ void NodeController::OnRequestIntroduction(const ports::NodeName& from_node,
// We don't know who they're talking about!
requestor->Introduce(name, ScopedInternalPlatformHandle());
} else {
PlatformChannelPair new_channel;
requestor->Introduce(name, new_channel.PassServerHandle());
new_friend->Introduce(from_node, new_channel.PassClientHandle());
PlatformChannel new_channel;
requestor->Introduce(
name, PlatformHandleToScopedInternalPlatformHandle(
new_channel.TakeLocalEndpoint().TakePlatformHandle()));
new_friend->Introduce(
from_node, PlatformHandleToScopedInternalPlatformHandle(
new_channel.TakeRemoteEndpoint().TakePlatformHandle()));
}
}
......
// Copyright 2014 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/edk/embedder/platform_channel_pair.h"
#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <utility>
#include <vector>
#include "base/containers/circular_deque.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
#include "base/macros.h"
#include "build/build_config.h"
#include "mojo/edk/embedder/platform_handle.h"
#include "mojo/edk/embedder/scoped_platform_handle.h"
#include "mojo/edk/test/test_utils.h"
#include "mojo/public/cpp/platform/socket_utils_posix.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace edk {
namespace {
void WaitReadable(InternalPlatformHandle h) {
struct pollfd pfds = {};
pfds.fd = h.handle;
pfds.events = POLLIN;
CHECK_EQ(poll(&pfds, 1, -1), 1);
}
class PlatformChannelPairPosixTest : public testing::Test {
public:
PlatformChannelPairPosixTest() {}
~PlatformChannelPairPosixTest() override {}
void SetUp() override {
// Make sure |SIGPIPE| isn't being ignored.
struct sigaction action = {};
action.sa_handler = SIG_DFL;
ASSERT_EQ(0, sigaction(SIGPIPE, &action, &old_action_));
}
void TearDown() override {
// Restore the |SIGPIPE| handler.
ASSERT_EQ(0, sigaction(SIGPIPE, &old_action_, nullptr));
}
private:
struct sigaction old_action_;
DISALLOW_COPY_AND_ASSIGN(PlatformChannelPairPosixTest);
};
TEST_F(PlatformChannelPairPosixTest, NoSigPipe) {
PlatformChannelPair channel_pair;
ScopedInternalPlatformHandle server_handle = channel_pair.PassServerHandle();
ScopedInternalPlatformHandle client_handle = channel_pair.PassClientHandle();
// Write to the client.
static const char kHello[] = "hello";
EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
write(client_handle.get().handle, kHello, sizeof(kHello)));
// Close the client.
client_handle.reset();
// Read from the server; this should be okay.
char buffer[100] = {};
EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
read(server_handle.get().handle, buffer, sizeof(buffer)));
EXPECT_STREQ(kHello, buffer);
// Try reading again.
ssize_t result = read(server_handle.get().handle, buffer, sizeof(buffer));
// We should probably get zero (for "end of file"), but -1 would also be okay.
EXPECT_TRUE(result == 0 || result == -1);
if (result == -1)
PLOG(WARNING) << "read (expected 0 for EOF)";
// Test our replacement for |write()|/|send()|.
result = SocketWrite(server_handle.get().handle, kHello, sizeof(kHello));
EXPECT_EQ(-1, result);
if (errno != EPIPE)
PLOG(WARNING) << "write (expected EPIPE)";
// Test our replacement for |writev()|/|sendv()|.
struct iovec iov[2] = {{const_cast<char*>(kHello), sizeof(kHello)},
{const_cast<char*>(kHello), sizeof(kHello)}};
result = SocketWritev(server_handle.get().handle, iov, 2);
EXPECT_EQ(-1, result);
if (errno != EPIPE)
PLOG(WARNING) << "write (expected EPIPE)";
}
TEST_F(PlatformChannelPairPosixTest, SendReceiveData) {
PlatformChannelPair channel_pair;
ScopedInternalPlatformHandle server_handle = channel_pair.PassServerHandle();
ScopedInternalPlatformHandle client_handle = channel_pair.PassClientHandle();
for (size_t i = 0; i < 10; i++) {
std::string send_string(1 << i, 'A' + i);
EXPECT_EQ(static_cast<ssize_t>(send_string.size()),
SocketWrite(server_handle.get().handle, send_string.data(),
send_string.size()));
WaitReadable(client_handle.get());
char buf[10000] = {};
std::vector<base::ScopedFD> received_fds;
ssize_t result = SocketRecvmsg(client_handle.get().handle, buf, sizeof(buf),
&received_fds);
EXPECT_EQ(static_cast<ssize_t>(send_string.size()), result);
EXPECT_EQ(send_string, std::string(buf, static_cast<size_t>(result)));
EXPECT_TRUE(received_fds.empty());
}
}
TEST_F(PlatformChannelPairPosixTest, SendReceiveFDs) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
static const char kHello[] = "hello";
PlatformChannelPair channel_pair;
ScopedInternalPlatformHandle server_handle = channel_pair.PassServerHandle();
ScopedInternalPlatformHandle client_handle = channel_pair.PassClientHandle();
const size_t kNumHandlesToSend = 64;
for (size_t i = 1; i < kNumHandlesToSend; i++) {
// Make |i| files, with the j-th file consisting of j copies of the digit
// |c|.
const char c = '0' + (i % 10);
std::vector<base::ScopedFD> fds;
for (size_t j = 1; j <= i; j++) {
base::FilePath unused;
base::ScopedFILE fp(
base::CreateAndOpenTemporaryFileInDir(temp_dir.GetPath(), &unused));
ASSERT_TRUE(fp);
ASSERT_EQ(j, fwrite(std::string(j, c).data(), 1, j, fp.get()));
fds.emplace_back(test::PlatformHandleFromFILE(std::move(fp)).TakeFD());
ASSERT_TRUE(fds.back().is_valid());
}
// Send the FDs (+ "hello").
struct iovec iov = {const_cast<char*>(kHello), sizeof(kHello)};
// We assume that the |sendmsg()| actually sends all the data.
EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
SendmsgWithHandles(server_handle.get().handle, &iov, 1,
std::move(fds)));
WaitReadable(client_handle.get());
char buf[10000] = {};
std::vector<base::ScopedFD> received_fds;
// We assume that the |recvmsg()| actually reads all the data.
EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
SocketRecvmsg(client_handle.get().handle, buf, sizeof(buf),
&received_fds));
EXPECT_STREQ(kHello, buf);
EXPECT_EQ(i, received_fds.size());
for (size_t j = 0; j < received_fds.size(); j++) {
base::ScopedFILE fp(test::FILEFromPlatformHandle(
PlatformHandle(std::move(received_fds[j])), "rb"));
ASSERT_TRUE(fp);
rewind(fp.get());
char read_buf[kNumHandlesToSend];
size_t bytes_read = fread(read_buf, 1, sizeof(read_buf), fp.get());
EXPECT_EQ(j + 1, bytes_read);
EXPECT_EQ(std::string(j + 1, c), std::string(read_buf, bytes_read));
}
}
}
TEST_F(PlatformChannelPairPosixTest, AppendReceivedFDs) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
static const char kHello[] = "hello";
PlatformChannelPair channel_pair;
ScopedInternalPlatformHandle server_handle = channel_pair.PassServerHandle();
ScopedInternalPlatformHandle client_handle = channel_pair.PassClientHandle();
const std::string file_contents("hello world");
{
base::FilePath unused;
base::ScopedFILE fp(
base::CreateAndOpenTemporaryFileInDir(temp_dir.GetPath(), &unused));
ASSERT_TRUE(fp);
ASSERT_EQ(file_contents.size(),
fwrite(file_contents.data(), 1, file_contents.size(), fp.get()));
std::vector<base::ScopedFD> fds(1);
fds[0] = test::PlatformHandleFromFILE(std::move(fp)).TakeFD();
ASSERT_TRUE(fds[0].is_valid());
// Send the FD (+ "hello").
struct iovec iov = {const_cast<char*>(kHello), sizeof(kHello)};
// We assume that the |sendmsg()| actually sends all the data.
EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
SendmsgWithHandles(server_handle.get().handle, &iov, 1, fds));
}
WaitReadable(client_handle.get());
std::vector<base::ScopedFD> fds;
char buf[100] = {};
// We assume that the |recvmsg()| actually reads all the data.
EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
SocketRecvmsg(client_handle.get().handle, buf, sizeof(buf), &fds));
EXPECT_STREQ(kHello, buf);
ASSERT_EQ(1u, fds.size());
EXPECT_TRUE(fds[0].is_valid());
{
base::ScopedFILE fp(
test::FILEFromPlatformHandle(PlatformHandle(std::move(fds[0])), "rb"));
ASSERT_TRUE(fp);
rewind(fp.get());
char read_buf[100];
size_t bytes_read = fread(read_buf, 1, sizeof(read_buf), fp.get());
EXPECT_EQ(file_contents.size(), bytes_read);
EXPECT_EQ(file_contents, std::string(read_buf, bytes_read));
}
}
} // namespace
} // namespace edk
} // namespace mojo
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment