| // Copyright 2021 The Fuchsia 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 "src/developer/forensics/feedback/main_service.h" |
| |
| #include <fuchsia/feedback/cpp/fidl.h> |
| #include <fuchsia/hardware/power/statecontrol/cpp/fidl.h> |
| #include <fuchsia/process/lifecycle/cpp/fidl.h> |
| #include <lib/fidl/cpp/interface_request.h> |
| #include <lib/fit/defer.h> |
| #include <lib/fit/function.h> |
| #include <lib/syslog/cpp/macros.h> |
| |
| #include <memory> |
| #include <optional> |
| #include <utility> |
| |
| #include "src/developer/forensics/feedback/annotations/device_id_provider.h" |
| #include "src/developer/forensics/feedback/annotations/provider.h" |
| #include "src/developer/forensics/feedback/reboot_log/graceful_shutdown_info.h" |
| #include "src/developer/forensics/feedback/redactor_factory.h" |
| #include "src/developer/forensics/feedback/stop_signals.h" |
| #include "src/developer/forensics/utils/cobalt/logger.h" |
| |
| namespace forensics::feedback { |
| namespace { |
| |
| using fuchsia::hardware::power::statecontrol::ShutdownWatcherRegister_RegisterWatcher_Result; |
| |
| std::unique_ptr<CachedAsyncAnnotationProvider> MakeDeviceIdProvider( |
| const std::optional<std::string>& local_device_id_path, async_dispatcher_t* dispatcher, |
| const std::shared_ptr<sys::ServiceDirectory>& services) { |
| if (local_device_id_path.has_value()) { |
| FX_LOGS(INFO) << "Using local device id provider"; |
| return std::make_unique<LocalDeviceIdProvider>(local_device_id_path.value()); |
| } |
| |
| FX_LOGS(INFO) << "Using remote device id provider"; |
| return std::make_unique<RemoteDeviceIdProvider>(dispatcher, services, |
| AnnotationProviders::AnnotationProviderBackoff()); |
| } |
| |
| std::set<std::string> RemoveExcludedAnnotations(const std::set<std::string>& default_annotations, |
| const std::set<std::string>& excluded_annotations) { |
| std::set<std::string> difference; |
| std::set_difference(default_annotations.begin(), default_annotations.end(), |
| excluded_annotations.begin(), excluded_annotations.end(), |
| std::inserter(difference, difference.begin())); |
| return difference; |
| } |
| |
| } // namespace |
| |
| MainService::MainService( |
| async_dispatcher_t* dispatcher, const std::shared_ptr<sys::ServiceDirectory> services, |
| timekeeper::Clock* clock, inspect::Node* inspect_root, cobalt::Logger* cobalt, |
| const Annotations& startup_annotations, |
| fidl::InterfaceRequest<fuchsia::process::lifecycle::Lifecycle> lifecycle_channel, |
| std::optional<std::string> dlog, Options options) |
| : dispatcher_(dispatcher), |
| executor_(dispatcher), |
| services_(services), |
| clock_(clock), |
| inspect_root_(inspect_root), |
| cobalt_(cobalt), |
| redactor_(RedactorFromConfig(inspect_root, options.build_type_config)), |
| inspect_node_manager_(inspect_root), |
| annotations_( |
| dispatcher_, services_, |
| /*allowlist=*/ |
| RemoveExcludedAnnotations( |
| options.feedback_data_options.snapshot_config.default_annotations, |
| options.feedback_data_options.snapshot_exclusion_config.excluded_annotations), |
| options.feedback_data_options.snapshot_exclusion_config.excluded_annotations, |
| startup_annotations, |
| MakeDeviceIdProvider(options.local_device_id_path, dispatcher_, services_)), |
| network_watcher_(dispatcher, *services), |
| feedback_data_(dispatcher_, services_, clock_, inspect_root_, cobalt_, redactor_.get(), |
| annotations_.GetAnnotationManager(), std::move(dlog), |
| options.feedback_data_options), |
| crash_reports_(dispatcher_, services_, clock_, inspect_root_, |
| annotations_.GetAnnotationManager(), feedback_data_.DataProvider(), |
| options.crash_reports_options), |
| last_reboot_(dispatcher_, cobalt_, redactor_.get(), crash_reports_.CrashReporter(), |
| options.last_reboot_options), |
| component_data_register_bindings_(dispatcher_, annotations_.ComponentDataRegister(), |
| &inspect_node_manager_, |
| "/fidl/fuchsia.feedback.ComponentDataRegister"), |
| crash_reporter_bindings_(dispatcher_, crash_reports_.CrashReporter(), &inspect_node_manager_, |
| "/fidl/fuchsia.feedback.CrashReporter"), |
| crash_register_bindings_(dispatcher_, crash_reports_.CrashRegister(), &inspect_node_manager_, |
| "/fidl/fuchsia.feedback.CrashReportingProductRegister"), |
| data_provider_bindings_(dispatcher_, feedback_data_.DataProvider(), &inspect_node_manager_, |
| "/fidl/fuchsia.feedback.DataProvider"), |
| last_reboot_info_bindings_(dispatcher_, last_reboot_.LastRebootInfoProvider(), |
| &inspect_node_manager_, |
| "/fidl/fuchsia.feedback.LastRebootInfoProvider") { |
| executor_.schedule_task( |
| WaitForLifecycleStop(dispatcher_, std::move(lifecycle_channel)) |
| .and_then([this](LifecycleStopSignal& signal) { |
| FX_LOGS(INFO) << "Received stop signal; stopping upload, but not exiting " |
| "to continue persisting new reports and logs"; |
| |
| crash_reports_.ShutdownImminent(); |
| feedback_data_.ShutdownImminent( |
| fit::defer_callback([s = std::move(signal)]() mutable { s.Respond(); })); |
| }) |
| .or_else([](const Error& error) { |
| FX_LOGS(ERROR) << "Won't receive lifecycle signal: " << ToString(error); |
| })); |
| |
| fidl::InterfaceHandle<fuchsia::hardware::power::statecontrol::ShutdownWatcher> handle; |
| executor_.schedule_task( |
| WaitForShutdownReason(dispatcher_, handle.NewRequest()) |
| .and_then([this, path = options.graceful_shutdown_info_write_path]( |
| GracefulShutdownInfoSignal& signal) { |
| const std::vector<GracefulShutdownReason> reasons = signal.Reasons(); |
| FX_LOGS(INFO) << "Received shutdown reasons '" << ToRawStrings(reasons) << "'"; |
| |
| WriteGracefulShutdownInfo(reasons, cobalt_, path); |
| signal.Respond(); |
| }) |
| .or_else([](const Error& error) { |
| FX_LOGS(ERROR) << "Won't receive shutdown reasons: " << ToString(error); |
| })); |
| |
| // We register ourselves with ShutdownWatcher using a fire-and-forget request that gives |
| // an endpoint to a long-lived connection we maintain. |
| // |
| // The ack response is ignored because the failures aren't expected unless the system is in a dire |
| // state. |
| services->Connect<fuchsia::hardware::power::statecontrol::ShutdownWatcherRegister>() |
| ->RegisterWatcher(std::move(handle), |
| [](const ShutdownWatcherRegister_RegisterWatcher_Result& result) {}); |
| |
| network_watcher_.Register( |
| fit::bind_member(&crash_reports_, &CrashReports::SetNetworkIsReachable)); |
| } |
| |
| template <> |
| ::fidl::InterfaceRequestHandler<fuchsia::feedback::LastRebootInfoProvider> |
| MainService::GetHandler() { |
| return [this](::fidl::InterfaceRequest<fuchsia::feedback::LastRebootInfoProvider> request) { |
| last_reboot_info_bindings_.AddBinding(std::move(request)); |
| }; |
| } |
| |
| template <> |
| ::fidl::InterfaceRequestHandler<fuchsia::feedback::CrashReporter> MainService::GetHandler() { |
| return [this](::fidl::InterfaceRequest<fuchsia::feedback::CrashReporter> request) { |
| crash_reporter_bindings_.AddBinding(std::move(request)); |
| }; |
| } |
| |
| template <> |
| ::fidl::InterfaceRequestHandler<fuchsia::feedback::CrashReportingProductRegister> |
| MainService::GetHandler() { |
| return |
| [this](::fidl::InterfaceRequest<fuchsia::feedback::CrashReportingProductRegister> request) { |
| crash_register_bindings_.AddBinding(std::move(request)); |
| }; |
| } |
| |
| template <> |
| ::fidl::InterfaceRequestHandler<fuchsia::feedback::ComponentDataRegister> |
| MainService::GetHandler() { |
| return [this](::fidl::InterfaceRequest<fuchsia::feedback::ComponentDataRegister> request) { |
| component_data_register_bindings_.AddBinding(std::move(request)); |
| }; |
| } |
| |
| template <> |
| ::fidl::InterfaceRequestHandler<fuchsia::feedback::DataProvider> MainService::GetHandler() { |
| return [this](::fidl::InterfaceRequest<fuchsia::feedback::DataProvider> request) { |
| data_provider_bindings_.AddBinding(std::move(request)); |
| }; |
| } |
| |
| } // namespace forensics::feedback |