blob: 90946e13e9d0bc580e1768daa1c91a3edcb17fdc [file] [log] [blame]
// Copyright 2019 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/cros_healthd/cros_healthd.h"
#include <cstdlib>
#include <memory>
#include <utility>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/run_loop.h>
#include <base/threading/thread_task_runner_handle.h>
#include <dbus/cros_healthd/dbus-constants.h>
#include <dbus/object_path.h>
#include <dbus/power_manager/dbus-constants.h>
#include <mojo/core/embedder/embedder.h>
#include <mojo/edk/embedder/embedder.h>
#include <mojo/edk/embedder/pending_process_connection.h>
#include "debugd/dbus-proxies.h"
#include "diagnostics/cros_healthd/cros_healthd_routine_service_impl.h"
namespace diagnostics {
CrosHealthd::CrosHealthd()
: DBusServiceDaemon(kCrosHealthdServiceName /* service_name */) {
// Set up only one |connection_| to D-Bus which cros_healthd can use to
// initiate the |debugd_proxy_| and a |power_manager_proxy_|.
dbus_bus_ = connection_.Connect();
CHECK(dbus_bus_) << "Failed to connect to the D-Bus system bus.";
debugd_proxy_ = std::make_unique<org::chromium::debugdProxy>(dbus_bus_);
power_manager_proxy_ = dbus_bus_->GetObjectProxy(
power_manager::kPowerManagerServiceName,
dbus::ObjectPath(power_manager::kPowerManagerServicePath));
battery_fetcher_ = std::make_unique<BatteryFetcher>(debugd_proxy_.get(),
power_manager_proxy_);
routine_service_ = std::make_unique<CrosHealthdRoutineServiceImpl>();
mojo_service_ = std::make_unique<CrosHealthdMojoService>(
battery_fetcher_.get(), routine_service_.get());
}
CrosHealthd::~CrosHealthd() = default;
int CrosHealthd::OnInit() {
VLOG(0) << "Starting";
const int exit_code = DBusServiceDaemon::OnInit();
if (exit_code != EXIT_SUCCESS)
return exit_code;
// Init the Mojo Embedder API.
mojo::core::Init();
ipc_support_ = std::make_unique<mojo::core::ScopedIPCSupport>(
base::ThreadTaskRunnerHandle::Get() /* io_thread_task_runner */,
mojo::core::ScopedIPCSupport::ShutdownPolicy::
CLEAN /* blocking shutdown */);
return EXIT_SUCCESS;
}
void CrosHealthd::RegisterDBusObjectsAsync(
brillo::dbus_utils::AsyncEventSequencer* sequencer) {
DCHECK(!dbus_object_);
dbus_object_ = std::make_unique<brillo::dbus_utils::DBusObject>(
nullptr /* object_manager */, bus_,
dbus::ObjectPath(kCrosHealthdServicePath));
brillo::dbus_utils::DBusInterface* dbus_interface =
dbus_object_->AddOrGetInterface(kCrosHealthdServiceInterface);
DCHECK(dbus_interface);
dbus_interface->AddSimpleMethodHandler(
kCrosHealthdBootstrapMojoConnectionMethod, base::Unretained(this),
&CrosHealthd::BootstrapMojoConnection);
dbus_object_->RegisterAsync(sequencer->GetHandler(
"Failed to register D-Bus object" /* descriptive_message */,
true /* failure_is_fatal */));
}
std::string CrosHealthd::BootstrapMojoConnection(const base::ScopedFD& mojo_fd,
bool is_chrome) {
VLOG(1) << "Received BootstrapMojoConnection D-Bus request";
if (!mojo_fd.is_valid()) {
constexpr char kInvalidFileDescriptorError[] =
"Invalid Mojo file descriptor";
LOG(ERROR) << kInvalidFileDescriptorError;
return kInvalidFileDescriptorError;
}
// 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()) {
constexpr char kFailedDuplicationError[] =
"Failed to duplicate the Mojo file descriptor";
PLOG(ERROR) << kFailedDuplicationError;
return kFailedDuplicationError;
}
if (!base::SetCloseOnExec(mojo_fd_copy.get())) {
constexpr char kFailedSettingFdCloexec[] =
"Failed to set FD_CLOEXEC on Mojo file descriptor";
PLOG(ERROR) << kFailedSettingFdCloexec;
return kFailedSettingFdCloexec;
}
std::string token;
chromeos::cros_healthd::mojom::CrosHealthdServiceRequest request;
if (is_chrome) {
// Connect to mojo in the requesting process.
mojo::edk::SetParentPipeHandle(mojo::edk::ScopedPlatformHandle(
mojo::edk::PlatformHandle(mojo_fd_copy.release())));
request =
mojo::MakeRequest<chromeos::cros_healthd::mojom::CrosHealthdService>(
mojo::edk::CreateChildMessagePipe(
kCrosHealthdMojoConnectionChannelToken));
} else {
// Create a unique token which will allow the requesting process to connect
// to us via mojo.
mojo::edk::PendingProcessConnection pending_connection;
mojo::ScopedMessagePipeHandle pipe =
pending_connection.CreateMessagePipe(&token);
pending_connection.Connect(
base::kNullProcessHandle,
mojo::edk::ConnectionParams(mojo::edk::ScopedPlatformHandle(
mojo::edk::PlatformHandle(mojo_fd_copy.release()))));
request =
mojo::MakeRequest<chromeos::cros_healthd::mojom::CrosHealthdService>(
std::move(pipe));
}
mojo_service_->AddBinding(std::move(request));
VLOG(1) << "Successfully bootstrapped Mojo connection";
return token;
}
void CrosHealthd::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
// guarantees it will support repeated bootstraps. Therefore, tear down and
// exit from our process and let upstart restart us again.
LOG(ERROR) << "Shutting down due to: " << debug_reason;
mojo_service_.reset();
Quit();
}
} // namespace diagnostics