| // Copyright 2024 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include <sysexits.h> | |
| #include <cstdlib> | |
| #include <memory> | |
| #include <optional> | |
| #include <base/files/file_path.h> | |
| #include <base/files/file_util.h> | |
| #include <base/logging.h> | |
| #include <brillo/blkdev_utils/storage_utils.h> | |
| #include <brillo/files/file_util.h> | |
| #include <brillo/flag_helper.h> | |
| #include <brillo/secure_blob.h> | |
| #include <vpd/vpd.h> | |
| #include "minios/log_store_manager.h" | |
| #include "minios/process_manager.h" | |
| #include "minios/utils.h" | |
| namespace minios { | |
| namespace { | |
| constexpr int kMiniOsAPartition = 9; | |
| constexpr int kMiniOsBPartition = 10; | |
| const base::FilePath kChromeOsStateful{"/mnt/stateful_partition/"}; | |
| struct ScopedUnmounterTraits { | |
| static bool InvalidValue() { return false; } | |
| static void Free(bool is_minios) { | |
| minios::UnmountStatefulPartition(std::make_shared<ProcessManager>()); | |
| } | |
| }; | |
| using ScopedUnmounter = base::ScopedGeneric<bool, ScopedUnmounterTraits>; | |
| } // namespace | |
| bool EraseStatefulLogs() { | |
| const auto running_from_minios_opt = IsRunningFromMiniOs(); | |
| // Return failure if we can't determine our environment. | |
| if (!running_from_minios_opt) | |
| return false; | |
| if (running_from_minios_opt.value() && | |
| !base::PathExists(kStatefulPath.Append(kUnencryptedMiniosPath)) && | |
| !MountStatefulPartition(std::make_shared<ProcessManager>())) { | |
| LOG(ERROR) << "Failed to mount stateful."; | |
| return false; | |
| } | |
| ScopedUnmounter unmounter(running_from_minios_opt.value()); | |
| const auto source_path = | |
| (running_from_minios_opt.value() ? kStatefulPath : kChromeOsStateful) | |
| .Append(kUnencryptedMiniosPath) | |
| .Append(kLogArchiveFile); | |
| return brillo::DeleteFile(source_path); | |
| } | |
| bool EraseLogs( | |
| std::vector<std::shared_ptr<minios::LogStoreManager>> log_store_managers) { | |
| bool result = true; | |
| if (!EraseStatefulLogs()) | |
| result = false; | |
| for (const auto& manager : log_store_managers) { | |
| if (!manager || !manager->ClearLogs()) | |
| result = false; | |
| } | |
| return result; | |
| } | |
| std::optional<bool> RetrieveLogs( | |
| const std::vector<std::shared_ptr<minios::LogStoreManager>>& | |
| log_store_managers, | |
| const std::shared_ptr<minios::LogStoreManager>& stateful_manager, | |
| const base::FilePath& dest_dir) { | |
| const auto key = GetLogStoreKey(std::make_shared<vpd::Vpd>()); | |
| if (!key) { | |
| LOG(WARNING) << "No key found, so no logs to fetch."; | |
| return false; | |
| } | |
| std::optional<bool> logs_retrieved = false; | |
| for (const auto& manager : log_store_managers) { | |
| if (!manager) { | |
| LOG(WARNING) << "Uninitialized manager."; | |
| logs_retrieved = std::nullopt; | |
| continue; | |
| } | |
| std::optional<bool> result; | |
| if (manager == stateful_manager) { | |
| const auto running_from_minios_opt = IsRunningFromMiniOs(); | |
| if (!running_from_minios_opt) { | |
| LOG(WARNING) << "Could not determine environment."; | |
| logs_retrieved = std::nullopt; | |
| continue; | |
| } | |
| const auto stateful_source_path = | |
| (running_from_minios_opt.value() ? kStatefulPath : kChromeOsStateful) | |
| .Append(kUnencryptedMiniosPath) | |
| .Append(kLogArchiveFile); | |
| if (running_from_minios_opt.value() && | |
| !base::PathExists(kStatefulPath.Append(kUnencryptedMiniosPath)) && | |
| !MountStatefulPartition(std::make_shared<ProcessManager>())) { | |
| LOG(ERROR) << "Failed to mount stateful."; | |
| logs_retrieved = std::nullopt; | |
| continue; | |
| } | |
| ScopedUnmounter unmounter(running_from_minios_opt.value()); | |
| result = manager->FetchLogs(LogStoreManager::LogDirection::Stateful, | |
| dest_dir, key.value(), stateful_source_path); | |
| } else { | |
| result = manager->FetchLogs(LogStoreManagerInterface::LogDirection::Disk, | |
| dest_dir, key.value()); | |
| } | |
| if (result && !result.value()) { | |
| // No logs found, keep searching. | |
| continue; | |
| } | |
| if (!result) { | |
| LOG(ERROR) << "Error fetching logs."; | |
| logs_retrieved = std::nullopt; | |
| continue; | |
| } | |
| return true; | |
| } | |
| return logs_retrieved; | |
| } | |
| bool ClearKey() { | |
| const auto vpd = std::make_shared<vpd::Vpd>(); | |
| const auto stored_key = GetLogStoreKey(vpd); | |
| if (stored_key && stored_key.value() != kNullKey) { | |
| return ClearLogStoreKey(vpd); | |
| } | |
| return true; | |
| } | |
| } // namespace minios | |
| int main(int argc, char** argv) { | |
| DEFINE_string(retrieve, "", "Retrieve stored logs to given path."); | |
| DEFINE_bool( | |
| erase, false, | |
| "Erase logs at source after retrieving logs. If specified without " | |
| "`retrieve`, will erase any unfetched logs on device."); | |
| DEFINE_bool(clear_key, false, | |
| "Clear logs store key from device if non-null key is stored."); | |
| brillo::FlagHelper::Init(argc, argv, "MiniOS Log Retrieval Tool"); | |
| auto log_store_factory = [](std::optional<int> partition) { | |
| std::shared_ptr<minios::LogStoreManager> manager; | |
| manager = partition | |
| ? std::make_shared<minios::LogStoreManager>(partition.value()) | |
| : std::make_shared<minios::LogStoreManager>(); | |
| if (!manager->Init()) | |
| manager.reset(); | |
| return manager; | |
| }; | |
| int exit_code = EXIT_SUCCESS; | |
| if (!FLAGS_retrieve.empty()) { | |
| const auto stateful_manager = log_store_factory(std::nullopt), | |
| slot_a_manager = log_store_factory(minios::kMiniOsAPartition), | |
| slot_b_manager = log_store_factory(minios::kMiniOsBPartition); | |
| const auto dest_dir = base::FilePath{FLAGS_retrieve}; | |
| if (!base::DirectoryExists(dest_dir)) { | |
| LOG(ERROR) << "Invalid retrieval destination=" << dest_dir; | |
| return EX_USAGE; | |
| } | |
| const auto retrieve_result = | |
| minios::RetrieveLogs({stateful_manager, slot_a_manager, slot_b_manager}, | |
| stateful_manager, dest_dir); | |
| if (!retrieve_result) | |
| exit_code = EXIT_FAILURE; | |
| if (FLAGS_erase && !minios::EraseLogs({slot_a_manager, slot_b_manager})) { | |
| exit_code = EXIT_FAILURE; | |
| } | |
| } else if (FLAGS_erase) { | |
| const auto slot_a_manager = log_store_factory(minios::kMiniOsAPartition), | |
| slot_b_manager = log_store_factory(minios::kMiniOsBPartition); | |
| if (!minios::EraseLogs({slot_a_manager, slot_b_manager})) | |
| exit_code = EXIT_FAILURE; | |
| } | |
| if (FLAGS_clear_key && !minios::ClearKey()) { | |
| exit_code = EXIT_FAILURE; | |
| } | |
| return exit_code; | |
| } |