// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_COMPONENTS_MOJO_BOOTSTRAP_PENDING_CONNECTION_MANAGER_H_
#define CHROMEOS_COMPONENTS_MOJO_BOOTSTRAP_PENDING_CONNECTION_MANAGER_H_
#include <string>
#include "base/compiler_specific.h"
#include "base/component_export.h"
#include "base/containers/flat_map.h"
#include "base/files/scoped_file.h"
#include "base/functional/callback.h"
#include "base/no_destructor.h"
#include "base/unguessable_token.h"
namespace mojo_bootstrap {
class PendingConnectionManagerTest;
// PendingConnectionManager is used to wait for a unix domain socket to be used
// for bootstrapping a Mojo connection. The typical use case is where a system
// service is started by Chrome, but only text arguments can be passed to the
// service startup. An example of this are filesystems mounted by cros-disks,
// where only an array of strings can be passed as options to the Mount() D-Bus
// method. This class is NOT needed if a socket end can be passed to the system
// service directly.
//
// To use this class:
// 1. Create and export a D-Bus service in Chrome containing a single method
// which is passed a string and FD. (eg. org.chromium.DriveFileStream).
// This method simply calls PendingConnectionManager::OpenIpcChannel().
// 2. Create an UnguessableToken and pass the string serialisation to the system
// service. Use PendingConnectionManager::ExpectOpenIpcChannel() to wait for
// an FD.
// 3. In the system service, create a socketpair() and pass one end over the
// D-Bus method created in step 1, along with the token.
//
// Sample usage:
// --- Chrome ---
// class MyClass {
// private:
// const base::UnguessableToken token_;
// mojo::OutgoingInvitation invitation_;
// };
//
// MyClass::MyClass() : token_(base::UnguessableToken::Create()) {}
//
// void MyClass::StartService() {
// // Use this message pipe to bind an InterfacePtr<> to the Mojo service.
// mojo::ScopedMessagePipeHandle bootstrap_handle =
// invitation_.AttachMessagePipe("myservice-bootstrap");
//
// base::UnguessableToken token = base::UnguessableToken::Create();
// PendingConnectionManager::Get().ExpectOpenIpcChannel(
// token_, base::BindOnce(&MyClass::AcceptConnection,
// base::Unretained(this)));
// StartMySystemService(token_.ToString());
// }
//
// void MyClass::AcceptConnection(base::ScopedFD handle) {
// mojo::OutgoingInvitation::Send(
// std::move(invitation_), base::kNullProcessHandle,
// mojo::PlatformChannelEndpoint(
// mojo::PlatformHandle(std::move(handle))));
// }
//
// void MyClass::MyExportedDbusConnectMethod(
// dbus::MethodCall* method_call,
// dbus::ExportedObject::ResponseSender response_sender) {
// std::string token = // Pop token passed to StartMySystemService()
// base::ScopedFD fd = // Pop FD
// CHECK(PendingConnectionManager::Get().OpenIpcChannel(
// token, std::move(fd)));
// }
//
// --- System Service ---
// // Returns an InterfaceRequest<> that can be used to bind the Mojo service
// // implementation.
// mojom::MyServiceRequest MyService::BootstrapMojo() {
// mojo::edk::PlatformChannelPair channel;
// org::chromium::MyChromeServiceProxy dbus_proxy(bus_, kServiceName);
// brillo::ErrorPtr error;
// CHECK(dbus_proxy.MyExportedDbusConnectMethod(
// token_, channel.PassClientHandle().get().handle, &error));
// mojo::edk::SetParentPipeHandle(channel.PassServerHandle());
//
// mojom::MyServiceRequest request;
// request.Bind(mojo::edk::CreateChildMessagePipe("myservice-bootstrap"));
// return request;
// }
class COMPONENT_EXPORT(MOJO_BOOTSTRAP) PendingConnectionManager {
public:
using OpenIpcChannelCallback = base::OnceCallback<void(base::ScopedFD)>;
static PendingConnectionManager& Get();
PendingConnectionManager(const PendingConnectionManager&) = delete;
PendingConnectionManager& operator=(const PendingConnectionManager&) = delete;
// Responds to a file descriptor request for |token| with |fd|. |token| is the
// UnguessableToken::ToString() representation of the |token| parameter to
// ExpectOpenIpcChannel().
bool OpenIpcChannel(const std::string& token, base::ScopedFD ipc_channel);
// Registers a callback that is run when a file descriptor is received for
// |token|.
void ExpectOpenIpcChannel(base::UnguessableToken token,
OpenIpcChannelCallback handler);
// Cancels the pending callback for |token|.
void CancelExpectedOpenIpcChannel(base::UnguessableToken token);
private:
friend class base::NoDestructor<PendingConnectionManager>;
friend class PendingConnectionManagerTest;
PendingConnectionManager();
~PendingConnectionManager();
base::flat_map<std::string, OpenIpcChannelCallback>
open_ipc_channel_callbacks_;
};
} // namespace mojo_bootstrap
#endif // CHROMEOS_COMPONENTS_MOJO_BOOTSTRAP_PENDING_CONNECTION_MANAGER_H_