| // 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/feedback_data.h" |
| |
| #include <lib/async/cpp/task.h> |
| #include <lib/fdio/spawn.h> |
| #include <lib/syslog/cpp/macros.h> |
| #include <lib/zx/channel.h> |
| #include <zircon/processargs.h> |
| #include <zircon/types.h> |
| |
| #include <memory> |
| |
| #include "src/developer/forensics/feedback/attachments/static_attachments.h" |
| #include "src/developer/forensics/feedback/constants.h" |
| #include "src/developer/forensics/feedback_data/constants.h" |
| #include "src/lib/files/path.h" |
| |
| namespace forensics::feedback { |
| |
| FeedbackData::FeedbackData(async_dispatcher_t* dispatcher, |
| std::shared_ptr<sys::ServiceDirectory> services, |
| timekeeper::Clock* clock, inspect::Node* inspect_root, |
| cobalt::Logger* cobalt, RedactorBase* redactor, |
| feedback::AnnotationManager* annotation_manager, Options options) |
| : dispatcher_(dispatcher), |
| services_(services), |
| clock_(clock), |
| cobalt_(cobalt), |
| inspect_node_manager_(inspect_root), |
| inspect_data_budget_(options.limit_inspect_data, &inspect_node_manager_, cobalt_), |
| attachment_providers_(dispatcher, services, clock, redactor, &inspect_data_budget_, |
| options.config.attachment_allowlist, GetStaticAttachments()), |
| data_provider_(dispatcher_, services_, clock_, redactor, options.is_first_instance, |
| options.config.annotation_allowlist, options.config.attachment_allowlist, |
| cobalt_, annotation_manager, attachment_providers_.GetAttachmentManager(), |
| &inspect_data_budget_), |
| data_provider_controller_() { |
| if (options.spawn_system_log_recorder) { |
| SpawnSystemLogRecorder(); |
| } |
| |
| if (options.delete_previous_boot_logs_time) { |
| DeletePreviousBootLogsAt(*options.delete_previous_boot_logs_time); |
| } |
| } |
| |
| void FeedbackData::Handle(::fidl::InterfaceRequest<fuchsia::feedback::DataProvider> request, |
| ::fit::function<void(zx_status_t)> error_handler) { |
| data_provider_connections_.AddBinding(&data_provider_, std::move(request), dispatcher_, |
| std::move(error_handler)); |
| } |
| void FeedbackData::Handle( |
| ::fidl::InterfaceRequest<fuchsia::feedback::DataProviderController> request, |
| ::fit::function<void(zx_status_t)> error_handler) { |
| data_provider_controller_connections_.AddBinding(&data_provider_controller_, std::move(request), |
| dispatcher_, std::move(error_handler)); |
| } |
| |
| fuchsia::feedback::DataProvider* FeedbackData::DataProvider() { return &data_provider_; } |
| |
| void FeedbackData::ShutdownImminent(::fit::deferred_callback stop_respond) { |
| system_log_recorder_lifecycle_.set_error_handler( |
| [stop_respond = std::move(stop_respond)](const zx_status_t status) mutable { |
| if (status != ZX_OK) { |
| FX_PLOGS(WARNING, status) << "Lost connection to system log recorder"; |
| } |
| |
| // |stop_respond| must explicitly be called otherwise it won't run until the error |
| // handler is destroyed (which doesn't happen). |
| stop_respond.call(); |
| }); |
| system_log_recorder_lifecycle_->Stop(); |
| } |
| |
| void FeedbackData::SpawnSystemLogRecorder() { |
| zx::channel controller_client, controller_server; |
| if (const auto status = zx::channel::create(0, &controller_client, &controller_server); |
| status != ZX_OK) { |
| FX_PLOGS(ERROR, status) |
| << "Failed to create system log recorder controller channel, logs will not be persisted"; |
| return; |
| } |
| |
| zx::channel lifecycle_client, lifecycle_server; |
| if (const auto status = zx::channel::create(0, &lifecycle_client, &lifecycle_server); |
| status != ZX_OK) { |
| FX_PLOGS(ERROR, status) |
| << "Failed to create system log recorder lifecycle channel, logs will not be persisted"; |
| return; |
| } |
| |
| const std::array<const char*, 2> argv = { |
| "system_log_recorder" /* process name */, |
| nullptr, |
| }; |
| const std::array actions = { |
| fdio_spawn_action_t{ |
| .action = FDIO_SPAWN_ACTION_ADD_HANDLE, |
| .h = |
| { |
| .id = PA_HND(PA_USER0, 0), |
| .handle = controller_server.release(), |
| }, |
| }, |
| fdio_spawn_action_t{ |
| .action = FDIO_SPAWN_ACTION_ADD_HANDLE, |
| .h = |
| { |
| .id = PA_HND(PA_USER1, 0), |
| .handle = lifecycle_server.release(), |
| }, |
| }, |
| }; |
| |
| zx_handle_t process; |
| char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH] = {}; |
| if (const zx_status_t status = fdio_spawn_etc( |
| ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, "/pkg/bin/system_log_recorder", argv.data(), |
| /*environ=*/nullptr, actions.size(), actions.data(), &process, err_msg); |
| status != ZX_OK) { |
| FX_PLOGS(ERROR, status) << "Failed to spawn system log recorder, logs will not be persisted: " |
| << err_msg; |
| return; |
| } |
| |
| data_provider_controller_.BindSystemLogRecorderController(std::move(controller_client), |
| dispatcher_); |
| system_log_recorder_lifecycle_.Bind(std::move(lifecycle_client), dispatcher_); |
| } |
| |
| void FeedbackData::DeletePreviousBootLogsAt(zx::duration uptime, |
| const std::string& previous_boot_logs_file) { |
| async::PostDelayedTask( |
| dispatcher_, |
| [this, previous_boot_logs_file] { |
| FX_LOGS(INFO) << "Deleting previous boot logs after 24 hours of device uptime"; |
| |
| attachment_providers_.GetAttachmentManager()->DropStaticAttachment( |
| feedback_data::kAttachmentLogSystemPrevious, Error::kCustom); |
| files::DeletePath(previous_boot_logs_file, /*recursive=*/true); |
| }, |
| // The previous boot logs are deleted after |uptime| of device uptime, not component uptime. |
| std::max(zx::sec(0), uptime - zx::nsec(clock_->Now().get()))); |
| } |
| |
| } // namespace forensics::feedback |