blob: 317ca08d60e5f61e6268d3ebf09a62c311ab0954 [file] [log] [blame]
// Copyright 2020 The Chromium OS 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 "diagnostics/wilco_dtc_supportd/mojo_service_factory.h"
#include <utility>
#include <base/files/file_util.h>
#include <base/posix/eintr_wrapper.h>
#include <mojo/public/cpp/bindings/interface_request.h>
#include <mojo/public/cpp/system/invitation.h>
#include <dbus/wilco_dtc_supportd/dbus-constants.h>
#include "diagnostics/wilco_dtc_supportd/mojo_service.h"
namespace diagnostics {
namespace {
MojoServiceFactory::MojoBindingPtr BindMojoServiceFactory(
MojoServiceFactory::WilcoServiceFactory* mojo_service_factory,
base::ScopedFD mojo_pipe_fd) {
DCHECK(mojo_service_factory);
DCHECK(mojo_pipe_fd.is_valid());
mojo::IncomingInvitation invitation =
mojo::IncomingInvitation::Accept(mojo::PlatformChannelEndpoint(
mojo::PlatformHandle(std::move(mojo_pipe_fd))));
mojo::ScopedMessagePipeHandle mojo_pipe_handle =
invitation.ExtractMessagePipe(
kWilcoDtcSupportdMojoConnectionChannelToken);
if (!mojo_pipe_handle.is_valid()) {
return nullptr;
}
return std::make_unique<mojo::Binding<
chromeos::wilco_dtc_supportd::mojom::WilcoDtcSupportdServiceFactory>>(
mojo_service_factory,
mojo::InterfaceRequest<
chromeos::wilco_dtc_supportd::mojom::WilcoDtcSupportdServiceFactory>(
std::move(mojo_pipe_handle)));
}
} // namespace
MojoServiceFactory::MojoServiceFactory(
MojoGrpcAdapter* grpc_adapter,
base::RepeatingClosure shutdown,
BindFactoryCallback bind_factory_callback)
: grpc_adapter_(grpc_adapter),
shutdown_(shutdown),
bind_factory_callback_(std::move(bind_factory_callback)) {
DCHECK(grpc_adapter_);
}
MojoServiceFactory::~MojoServiceFactory() = default;
MojoService* MojoServiceFactory::Get() const {
return mojo_service_.get();
}
base::Optional<std::string> MojoServiceFactory::BootstrapMojoConnection(
const base::ScopedFD& mojo_fd) {
if (!mojo_fd.is_valid()) {
LOG(ERROR) << "Invalid Mojo file descriptor";
return "Invalid file descriptor";
}
// We need a file descriptor that stays alive after the current method
// finishes, but libbrillo's D-Bus wrappers currently don't support passing
// base::ScopedFD by value.
base::ScopedFD mojo_fd_copy(HANDLE_EINTR(dup(mojo_fd.get())));
if (!mojo_fd_copy.is_valid()) {
PLOG(ERROR) << "Failed to duplicate the Mojo file descriptor";
return "Failed to duplicate file descriptor";
}
return Start(std::move(mojo_fd_copy));
}
base::Optional<std::string> MojoServiceFactory::Start(
base::ScopedFD mojo_pipe_fd) {
DCHECK(mojo_pipe_fd.is_valid());
if (mojo_service_bind_attempted_) {
// This should not normally be triggered, since the other endpoint - the
// browser process - should bootstrap the Mojo connection only once, and
// when that process is killed the Mojo shutdown notification should have
// been received earlier. But handle this case to be on the safe side. After
// our restart the browser process is expected to invoke the bootstrapping
// again.
ShutdownDueToMojoError(
"Repeated Mojo bootstrap request received" /* debug_reason */);
return "Mojo connection was already bootstrapped";
}
if (!base::SetCloseOnExec(mojo_pipe_fd.get())) {
PLOG(ERROR) << "Failed to set FD_CLOEXEC on Mojo file descriptor";
return "Failed to set FD_CLOEXEC";
}
mojo_service_bind_attempted_ = true;
mojo_service_factory_binding_ =
std::move(bind_factory_callback_).Run(this, std::move(mojo_pipe_fd));
if (!mojo_service_factory_binding_) {
ShutdownDueToMojoError("Mojo bootstrap failed" /* debug_reason */);
return "Failed to bootstrap Mojo";
}
mojo_service_factory_binding_->set_connection_error_handler(base::Bind(
&MojoServiceFactory::ShutdownDueToMojoError, base::Unretained(this),
"Mojo connection error" /* debug_reason */));
LOG(INFO) << "Successfully bootstrapped Mojo connection";
return base::nullopt;
}
MojoServiceFactory::BindFactoryCallback
MojoServiceFactory::CreateBindFactoryCallback() {
return base::BindOnce(&BindMojoServiceFactory);
}
void MojoServiceFactory::ShutdownDueToMojoError(
const std::string& debug_reason) {
// Our daemon has to be restarted to be prepared for future Mojo connection
// bootstraps. We can't do this without a restart since Mojo EDK gives no
// guarantee to support repeated bootstraps. Therefore tear down and exit from
// our process and let upstart to restart us again.
LOG(INFO) << "Shutting down due to: " << debug_reason;
mojo_service_.reset();
mojo_service_factory_binding_.reset();
shutdown_.Run();
}
void MojoServiceFactory::GetService(
chromeos::wilco_dtc_supportd::mojom::WilcoDtcSupportdServiceRequest service,
chromeos::wilco_dtc_supportd::mojom::WilcoDtcSupportdClientPtr client,
GetServiceCallback callback) {
// Mojo guarantees that these parameters are nun-null (see
// VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE).
DCHECK(service.is_pending());
DCHECK(client);
if (mojo_service_) {
LOG(WARNING) << "GetService Mojo method called multiple times";
// We should not normally be called more than once, so don't bother with
// trying to reuse objects from the previous call. However, make sure we
// don't have duplicate instances of the service at any moment of time.
mojo_service_.reset();
}
// Create an instance of MojoService that will handle incoming
// Mojo calls. Pass |service| to it to fulfill the remote endpoint's request,
// allowing it to call into |mojo_service_|. Pass also |client| to allow
// |mojo_service_| to do calls in the opposite direction.
mojo_service_ = std::make_unique<MojoService>(
grpc_adapter_, std::move(service), std::move(client));
std::move(callback).Run();
}
} // namespace diagnostics