| // 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 <fuchsia/feedback/cpp/fidl.h> |
| #include <fuchsia/process/lifecycle/cpp/fidl.h> |
| #include <lib/async/cpp/executor.h> |
| #include <lib/fidl/cpp/interface_request.h> |
| #include <lib/fit/defer.h> |
| #include <lib/fpromise/promise.h> |
| #include <lib/syslog/cpp/log_settings.h> |
| #include <lib/syslog/cpp/macros.h> |
| #include <zircon/processargs.h> |
| |
| #include <cstdlib> |
| #include <memory> |
| |
| #include <fbl/unique_fd.h> |
| |
| #include "src/developer/forensics/crash_reports/default_annotations.h" |
| #include "src/developer/forensics/feedback/annotations/startup_annotations.h" |
| #include "src/developer/forensics/feedback/config.h" |
| #include "src/developer/forensics/feedback/constants.h" |
| #include "src/developer/forensics/feedback/main_service.h" |
| #include "src/developer/forensics/feedback/migration/utils/log.h" |
| #include "src/developer/forensics/feedback/migration/utils/migrate.h" |
| #include "src/developer/forensics/feedback/namespace_init.h" |
| #include "src/developer/forensics/feedback/reboot_log/annotations.h" |
| #include "src/developer/forensics/feedback/reboot_log/reboot_log.h" |
| #include "src/developer/forensics/feedback/redactor_factory.h" |
| #include "src/developer/forensics/feedback_data/default_annotations.h" |
| #include "src/developer/forensics/utils/cobalt/logger.h" |
| #include "src/developer/forensics/utils/component/component.h" |
| #include "src/lib/files/file.h" |
| #include "src/lib/fxl/strings/split_string.h" |
| #include "src/lib/uuid/uuid.h" |
| |
| namespace forensics::feedback { |
| |
| int main() { |
| syslog::SetTags({"forensics", "feedback"}); |
| |
| // Delay serving the outgoing directory because the migration happens asynchronously and the |
| // component's services cannot be used until the migration has completed. |
| forensics::component::Component component(/*lazy_outgoing_dir=*/true); |
| |
| async::Executor executor(component.Dispatcher()); |
| |
| auto crash_reports_config = GetCrashReportsConfig(); |
| if (!crash_reports_config) { |
| FX_LOGS(FATAL) << "Failed to get config for crash reporting"; |
| return EXIT_FAILURE; |
| } |
| |
| auto feedback_data_config = GetFeedbackDataConfig(); |
| if (!feedback_data_config) { |
| FX_LOGS(FATAL) << "Failed to get config for feedback data"; |
| return EXIT_FAILURE; |
| } |
| |
| auto migration_log = MigrationLog::FromFile("/data/migration_log.json"); |
| if (!migration_log) { |
| FX_LOGS(ERROR) << "Failed to create migration log"; |
| } |
| |
| // Don't allocate the |cobalt| and |main_service|until after the migration has completed and the |
| // component can function properly. |
| std::unique_ptr<cobalt::Logger> cobalt; |
| std::unique_ptr<MainService> main_service; |
| |
| executor.schedule_task( |
| MigrateData(component.Dispatcher(), component.Services(), migration_log, |
| kDirectoryMigratorResponeTimeout) |
| .and_then( |
| [&](const std::tuple<::fpromise::result<void, Error>, ::fpromise::result<void, Error>, |
| ::fpromise::result<void, Error>>& results) { |
| std::map<std::string, std::string> migration_failures; |
| if (const auto result = std::get<0>(results); !result.is_ok()) { |
| FX_LOGS(ERROR) |
| << "Experienced errors while migrating last reboot, continuing as normal: " |
| << ToString(result.error()); |
| migration_failures["debug.last_reboot.migration-failure"] = |
| ToString(result.error()); |
| } |
| |
| if (const auto result = std::get<1>(results); !result.is_ok()) { |
| FX_LOGS(ERROR) |
| << "Experienced errors while migrating crash reports, continuing as normal: " |
| << ToString(result.error()); |
| migration_failures["debug.crash_reports.migration-failure"] = |
| ToString(result.error()); |
| } |
| |
| if (const auto result = std::get<2>(results); !result.is_ok()) { |
| FX_LOGS(ERROR) |
| << "Experienced errors while migrating feedback data, continuing as normal: " |
| << ToString(result.error()); |
| migration_failures["debug.feedback_data.migration-failure"] = |
| ToString(result.error()); |
| } |
| |
| if (migration_log) { |
| if (!migration_log->Contains(MigrationLog::Component::kLastReboot)) { |
| migration_log->Set(MigrationLog::Component::kLastReboot); |
| } else { |
| FX_LOGS(INFO) << "Already migrated last reboot"; |
| } |
| |
| if (!migration_log->Contains(MigrationLog::Component::kCrashReports)) { |
| migration_log->Set(MigrationLog::Component::kCrashReports); |
| } else { |
| FX_LOGS(INFO) << "Already migrated crash reports"; |
| } |
| |
| if (!migration_log->Contains(MigrationLog::Component::kFeedbackData)) { |
| migration_log->Set(MigrationLog::Component::kFeedbackData); |
| } else { |
| FX_LOGS(INFO) << "Already migrated feedback data"; |
| } |
| } |
| |
| cobalt = std::make_unique<cobalt::Logger>(component.Dispatcher(), |
| component.Services(), component.Clock()); |
| |
| if (component.IsFirstInstance()) { |
| MovePreviousRebootReason(); |
| CreatePreviousLogsFile(cobalt.get()); |
| MoveAndRecordBootId(uuid::Generate()); |
| if (std::string build_version; |
| files::ReadFileToString(kBuildVersionPath, &build_version)) { |
| MoveAndRecordBuildVersion(build_version); |
| } |
| } |
| |
| auto reboot_log = RebootLog::ParseRebootLog("/boot/log/last-panic.txt", |
| kPreviousGracefulRebootReasonFile, |
| TestAndSetNotAFdr()); |
| |
| const bool limit_inspect_data = files::IsFile(kLimitInspectDataPath); |
| const bool spawn_system_log_recorder = |
| !files::IsFile(kDoNotLaunchSystemLogRecorder); |
| |
| std::optional<std::string> local_device_id_path = kDeviceIdPath; |
| if (files::IsFile(kUseRemoteDeviceIdProviderPath)) { |
| local_device_id_path = std::nullopt; |
| } |
| |
| std::optional<zx::duration> delete_previous_boot_logs_time(std::nullopt); |
| if (files::IsFile(kPreviousLogsFilePath)) { |
| delete_previous_boot_logs_time = zx::hour(24); |
| } |
| |
| const auto startup_annotations = GetStartupAnnotations(reboot_log); |
| |
| main_service = std::make_unique<MainService>( |
| component.Dispatcher(), component.Services(), component.Clock(), |
| component.InspectRoot(), cobalt.get(), startup_annotations, |
| MainService::Options{ |
| local_device_id_path, |
| LastReboot::Options{ |
| .is_first_instance = component.IsFirstInstance(), |
| .reboot_log = reboot_log, |
| .graceful_reboot_reason_write_path = kCurrentGracefulRebootReasonFile, |
| .oom_crash_reporting_delay = kOOMCrashReportingDelay, |
| }, |
| CrashReports::Options{ |
| .config = *crash_reports_config, |
| .snapshot_manager_max_annotations_size = kSnapshotAnnotationsMaxSize, |
| .snapshot_manager_max_archives_size = kSnapshotArchivesMaxSize, |
| .snapshot_manager_window_duration = kSnapshotSharedRequestWindow, |
| }, |
| FeedbackData::Options{ |
| .config = *feedback_data_config, |
| .is_first_instance = component.IsFirstInstance(), |
| .limit_inspect_data = limit_inspect_data, |
| .spawn_system_log_recorder = spawn_system_log_recorder, |
| .delete_previous_boot_logs_time = delete_previous_boot_logs_time, |
| }}); |
| |
| if (!migration_failures.empty()) { |
| main_service->ReportMigrationError(migration_failures); |
| } |
| |
| // The component is now ready to serve its outgoing directory and serve requests. |
| component.AddPublicService( |
| main_service->GetHandler<fuchsia::feedback::LastRebootInfoProvider>()); |
| component.AddPublicService( |
| main_service->GetHandler<fuchsia::feedback::CrashReporter>()); |
| component.AddPublicService( |
| main_service->GetHandler<fuchsia::feedback::CrashReportingProductRegister>()); |
| component.AddPublicService( |
| main_service->GetHandler<fuchsia::feedback::ComponentDataRegister>()); |
| component.AddPublicService( |
| main_service->GetHandler<fuchsia::feedback::DataProvider>()); |
| component.AddPublicService( |
| main_service->GetHandler<fuchsia::feedback::DataProviderController>()); |
| |
| zx::channel lifecycle_channel(zx_take_startup_handle(PA_LIFECYCLE)); |
| |
| component.OnStopSignal( |
| ::fidl::InterfaceRequest<fuchsia::process::lifecycle::Lifecycle>( |
| std::move(lifecycle_channel)), |
| [&](::fit::deferred_callback stop_respond) { |
| FX_LOGS(INFO) << "Received stop signal; stopping upload, but not exiting " |
| "to continue persisting new reports and logs"; |
| main_service->ShutdownImminent(std::move(stop_respond)); |
| }); |
| })); |
| |
| component.RunLoop(); |
| return EXIT_SUCCESS; |
| } |
| |
| } // namespace forensics::feedback |