blob: 6ae2f6b4610484ea59183a57b282080357a53f93 [file] [log] [blame]
// 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