Commit eca8c747 authored by Ken Rockot's avatar Ken Rockot Committed by Commit Bot

Mojo: Add C++ invitation API

Bug: 844764
Change-Id: I73023a683708249f2b3bb139e616e2bef1df1b1e
Reviewed-on: https://chromium-review.googlesource.com/1067749Reviewed-by: default avatarJay Civelli <jcivelli@chromium.org>
Commit-Queue: Ken Rockot <rockot@chromium.org>
Cr-Commit-Position: refs/heads/master@{#561897}
parent 6acb04bc
...@@ -38,6 +38,8 @@ component("system") { ...@@ -38,6 +38,8 @@ component("system") {
"handle_signal_tracker.cc", "handle_signal_tracker.cc",
"handle_signal_tracker.h", "handle_signal_tracker.h",
"handle_signals_state.h", "handle_signals_state.h",
"invitation.cc",
"invitation.h",
"message.h", "message.h",
"message_pipe.cc", "message_pipe.cc",
"message_pipe.h", "message_pipe.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.
#include "mojo/public/cpp/system/invitation.h"
#include "base/numerics/safe_conversions.h"
#include "build/build_config.h"
#include "mojo/public/c/system/invitation.h"
#include "mojo/public/c/system/platform_handle.h"
#include "mojo/public/cpp/system/platform_handle.h"
namespace mojo {
namespace {
void ProcessHandleToMojoProcessHandle(base::ProcessHandle target_process,
MojoPlatformProcessHandle* handle) {
handle->struct_size = sizeof(*handle);
#if defined(OS_WIN)
handle->value =
static_cast<uint64_t>(reinterpret_cast<uintptr_t>(target_process));
#else
handle->value = static_cast<uint64_t>(target_process);
#endif
}
void PlatformHandleToTransportEndpoint(
PlatformHandle platform_handle,
MojoPlatformHandle* endpoint_handle,
MojoInvitationTransportEndpoint* endpoint) {
PlatformHandleToMojoPlatformHandle(std::move(platform_handle),
endpoint_handle);
CHECK_NE(endpoint_handle->type, MOJO_PLATFORM_HANDLE_TYPE_INVALID);
endpoint->struct_size = sizeof(*endpoint);
endpoint->num_platform_handles = 1;
endpoint->platform_handles = endpoint_handle;
}
void RunErrorCallback(uintptr_t context,
const MojoProcessErrorDetails* details) {
auto* callback = reinterpret_cast<ProcessErrorCallback*>(context);
std::string error_message;
if (details->error_message) {
error_message =
std::string(details->error_message, details->error_message_length - 1);
}
callback->Run(error_message);
if (details->flags & MOJO_PROCESS_ERROR_FLAG_DISCONNECTED)
delete callback;
}
void SendInvitation(ScopedInvitationHandle invitation,
base::ProcessHandle target_process,
PlatformHandle endpoint_handle,
MojoInvitationTransportType transport_type,
const ProcessErrorCallback& error_callback) {
MojoPlatformProcessHandle process_handle;
ProcessHandleToMojoProcessHandle(target_process, &process_handle);
MojoPlatformHandle platform_handle;
MojoInvitationTransportEndpoint endpoint;
PlatformHandleToTransportEndpoint(std::move(endpoint_handle),
&platform_handle, &endpoint);
endpoint.type = transport_type;
MojoProcessErrorHandler error_handler = nullptr;
uintptr_t error_handler_context = 0;
if (error_callback) {
error_handler = &RunErrorCallback;
// NOTE: The allocated callback is effectively owned by the error handler,
// which will delete it on the final invocation for this context (i.e.
// process disconnection).
error_handler_context =
reinterpret_cast<uintptr_t>(new ProcessErrorCallback(error_callback));
}
MojoResult result =
MojoSendInvitation(invitation.get().value(), &process_handle, &endpoint,
error_handler, error_handler_context, nullptr);
// If successful, the invitation handle is already closed for us.
if (result == MOJO_RESULT_OK)
ignore_result(invitation.release());
}
} // namespace
OutgoingInvitation::OutgoingInvitation() {
MojoHandle invitation_handle;
MojoResult result = MojoCreateInvitation(nullptr, &invitation_handle);
DCHECK_EQ(result, MOJO_RESULT_OK);
handle_.reset(InvitationHandle(invitation_handle));
}
OutgoingInvitation::OutgoingInvitation(OutgoingInvitation&& other) = default;
OutgoingInvitation::~OutgoingInvitation() = default;
OutgoingInvitation& OutgoingInvitation::operator=(OutgoingInvitation&& other) =
default;
ScopedMessagePipeHandle OutgoingInvitation::AttachMessagePipe(
base::StringPiece name) {
DCHECK(!name.empty());
DCHECK(base::IsValueInRangeForNumericType<uint32_t>(name.size()));
MojoHandle message_pipe_handle;
MojoResult result = MojoAttachMessagePipeToInvitation(
handle_.get().value(), name.data(), static_cast<uint32_t>(name.size()),
nullptr, &message_pipe_handle);
DCHECK_EQ(MOJO_RESULT_OK, result);
return ScopedMessagePipeHandle(MessagePipeHandle(message_pipe_handle));
}
ScopedMessagePipeHandle OutgoingInvitation::AttachMessagePipe(uint64_t name) {
return AttachMessagePipe(
base::StringPiece(reinterpret_cast<const char*>(&name), sizeof(name)));
}
ScopedMessagePipeHandle OutgoingInvitation::ExtractMessagePipe(
base::StringPiece name) {
DCHECK(!name.empty());
DCHECK(base::IsValueInRangeForNumericType<uint32_t>(name.size()));
MojoHandle message_pipe_handle;
MojoResult result = MojoExtractMessagePipeFromInvitation(
handle_.get().value(), name.data(), static_cast<uint32_t>(name.size()),
nullptr, &message_pipe_handle);
DCHECK_EQ(MOJO_RESULT_OK, result);
return ScopedMessagePipeHandle(MessagePipeHandle(message_pipe_handle));
}
ScopedMessagePipeHandle OutgoingInvitation::ExtractMessagePipe(uint64_t name) {
return ExtractMessagePipe(
base::StringPiece(reinterpret_cast<const char*>(&name), sizeof(name)));
}
// static
void OutgoingInvitation::Send(OutgoingInvitation invitation,
base::ProcessHandle target_process,
PlatformChannelEndpoint channel_endpoint,
const ProcessErrorCallback& error_callback) {
SendInvitation(std::move(invitation.handle_), target_process,
channel_endpoint.TakePlatformHandle(),
MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL, error_callback);
}
// static
void OutgoingInvitation::Send(OutgoingInvitation invitation,
base::ProcessHandle target_process,
PlatformChannelServerEndpoint server_endpoint,
const ProcessErrorCallback& error_callback) {
SendInvitation(std::move(invitation.handle_), target_process,
server_endpoint.TakePlatformHandle(),
MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER, error_callback);
}
IncomingInvitation::IncomingInvitation() = default;
IncomingInvitation::IncomingInvitation(IncomingInvitation&& other) = default;
IncomingInvitation::IncomingInvitation(ScopedInvitationHandle handle)
: handle_(std::move(handle)) {}
IncomingInvitation::~IncomingInvitation() = default;
IncomingInvitation& IncomingInvitation::operator=(IncomingInvitation&& other) =
default;
// static
IncomingInvitation IncomingInvitation::Accept(
PlatformChannelEndpoint channel_endpoint) {
MojoPlatformHandle endpoint_handle;
PlatformHandleToMojoPlatformHandle(channel_endpoint.TakePlatformHandle(),
&endpoint_handle);
CHECK_NE(endpoint_handle.type, MOJO_PLATFORM_HANDLE_TYPE_INVALID);
MojoInvitationTransportEndpoint transport_endpoint;
transport_endpoint.struct_size = sizeof(transport_endpoint);
transport_endpoint.type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL;
transport_endpoint.num_platform_handles = 1;
transport_endpoint.platform_handles = &endpoint_handle;
MojoHandle invitation_handle;
MojoResult result =
MojoAcceptInvitation(&transport_endpoint, nullptr, &invitation_handle);
if (result != MOJO_RESULT_OK)
return IncomingInvitation();
return IncomingInvitation(
ScopedInvitationHandle(InvitationHandle(invitation_handle)));
}
ScopedMessagePipeHandle IncomingInvitation::ExtractMessagePipe(
base::StringPiece name) {
DCHECK(!name.empty());
DCHECK(base::IsValueInRangeForNumericType<uint32_t>(name.size()));
DCHECK(handle_.is_valid());
MojoHandle message_pipe_handle;
MojoResult result = MojoExtractMessagePipeFromInvitation(
handle_.get().value(), name.data(), static_cast<uint32_t>(name.size()),
nullptr, &message_pipe_handle);
DCHECK_EQ(MOJO_RESULT_OK, result);
return ScopedMessagePipeHandle(MessagePipeHandle(message_pipe_handle));
}
ScopedMessagePipeHandle IncomingInvitation::ExtractMessagePipe(uint64_t name) {
return ExtractMessagePipe(
base::StringPiece(reinterpret_cast<const char*>(&name), sizeof(name)));
}
} // 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_PUBLIC_CPP_SYSTEM_INVITATION_H_
#define MOJO_PUBLIC_CPP_SYSTEM_INVITATION_H_
#include <cstdint>
#include <string>
#include "base/callback.h"
#include "base/component_export.h"
#include "base/macros.h"
#include "base/process/process_handle.h"
#include "base/strings/string_piece.h"
#include "mojo/public/cpp/platform/platform_channel_endpoint.h"
#include "mojo/public/cpp/platform/platform_channel_server_endpoint.h"
#include "mojo/public/cpp/system/handle.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "mojo/public/cpp/system/system_export.h"
namespace mojo {
// A callback which may be provided when sending an invitation to another
// process. In the event of any validation errors regarding messages from that
// process (reported via MojoNotifyBadMessage etc and related helpers), the
// callback will be invoked.
using ProcessErrorCallback = base::RepeatingCallback<void(const std::string&)>;
// A strongly-typed representation of a |MojoHandle| for an invitation.
class InvitationHandle : public Handle {
public:
InvitationHandle() {}
explicit InvitationHandle(MojoHandle value) : Handle(value) {}
// Copying and assignment allowed.
};
static_assert(sizeof(InvitationHandle) == sizeof(Handle),
"Bad size for C++ InvitationHandle");
using ScopedInvitationHandle = ScopedHandleBase<InvitationHandle>;
static_assert(sizeof(ScopedInvitationHandle) == sizeof(InvitationHandle),
"Bad size for C++ ScopedInvitationHandle");
// An OutgoingInvitation is used to invite another process to join the calling
// process's IPC network.
//
// Typical use involves constructing a |PlatformChannel| and using one end to
// send the invitation (see |Send()| below) while passing the other to a child
// process.
//
// This may also be used with the server endpoint of a |NamedPlatformChannel|.
class MOJO_CPP_SYSTEM_EXPORT OutgoingInvitation {
public:
OutgoingInvitation();
OutgoingInvitation(OutgoingInvitation&& other);
~OutgoingInvitation();
OutgoingInvitation& operator=(OutgoingInvitation&& other);
// Creates a new message pipe, attaching one end to this invitation and
// returning the other end to the caller. The invitee can extract the
// attached endpoint (see |IncomingInvitation|) thus establishing end-to-end
// Mojo communication.
//
// |name| is an arbitrary value that must be used by the invitee to extract
// the corresponding attached endpoint.
ScopedMessagePipeHandle AttachMessagePipe(base::StringPiece name);
// Same as above but allows use of an integer name for convenience.
ScopedMessagePipeHandle AttachMessagePipe(uint64_t name);
// Extracts an attached pipe. Note that this is not typically useful, but it
// is potentially necessary in cases where a caller wants to, e.g., abort
// launching another process and recover a pipe endpoint they had previously
// attached.
ScopedMessagePipeHandle ExtractMessagePipe(base::StringPiece name);
// Same as above but allows use of an integer name for convenience.
ScopedMessagePipeHandle ExtractMessagePipe(uint64_t name);
// Sends |invitation| to another process via |channel_endpoint|, which should
// correspond to the local endpoint taken from a |PlatformChannel|.
//
// |process_handle| is a handle to the destination process if known. If not
// provided, IPC may be limited on some platforms (namely Mac and Windows) due
// to an inability to transfer system handles across the boundary.
static void Send(OutgoingInvitation invitation,
base::ProcessHandle target_process,
PlatformChannelEndpoint channel_endpoint,
const ProcessErrorCallback& error_callback = {});
// Similar to above, but sends |invitation| via |server_endpoint|, which
// should correspond to a |PlatformChannelServerEndpoint| taken from a
// |NamedPlatformChannel|.
static void Send(OutgoingInvitation invitation,
base::ProcessHandle target_process,
PlatformChannelServerEndpoint server_endpoint,
const ProcessErrorCallback& error_callback = {});
private:
ScopedInvitationHandle handle_;
DISALLOW_COPY_AND_ASSIGN(OutgoingInvitation);
};
// An IncomingInvitation can be accepted by an invited process by calling
// |IncomingInvitation::Accept()|. Once accepted, the invitation can be used
// to extract attached message pipes by name.
class MOJO_CPP_SYSTEM_EXPORT IncomingInvitation {
public:
IncomingInvitation();
IncomingInvitation(IncomingInvitation&& other);
explicit IncomingInvitation(ScopedInvitationHandle handle);
~IncomingInvitation();
IncomingInvitation& operator=(IncomingInvitation&& other);
// Accepts an incoming invitation from |channel_endpoint|. If the invitation
// was sent using one end of a |PlatformChannel|, |channel_endpoint| should be
// the other end of that channel. If the invitation was sent using a
// |PlatformChannelServerEndpoint|, then |channel_endpoint| should be created
// by |NamedPlatformChannel::ConnectToServer|.
static IncomingInvitation Accept(PlatformChannelEndpoint channel_endpoint);
// Extracts an attached message pipe from this invitation. This may succeed
// even if no such pipe was attached, though the extracted pipe will
// eventually observe peer closure.
ScopedMessagePipeHandle ExtractMessagePipe(base::StringPiece name);
// Same as above but allows use of an integer name for convenience.
ScopedMessagePipeHandle ExtractMessagePipe(uint64_t name);
private:
ScopedInvitationHandle handle_;
DISALLOW_COPY_AND_ASSIGN(IncomingInvitation);
};
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_SYSTEM_INVITATION_H_
...@@ -18,11 +18,16 @@ source_set("tests") { ...@@ -18,11 +18,16 @@ source_set("tests") {
"wait_unittest.cc", "wait_unittest.cc",
] ]
if (!is_ios) {
sources += [ "invitation_unittest.cc" ]
}
deps = [ deps = [
"//base", "//base",
"//base/test:test_support", "//base/test:test_support",
"//mojo/edk/test:test_support", "//mojo/edk/test:test_support",
"//mojo/public/c/system/tests", "//mojo/public/c/system/tests",
"//mojo/public/cpp/platform",
"//mojo/public/cpp/system", "//mojo/public/cpp/system",
"//mojo/public/cpp/test_support:test_utils", "//mojo/public/cpp/test_support:test_utils",
"//testing/gtest", "//testing/gtest",
......
This diff is collapsed.
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