blob: 126c85fac835e36d5d207496e1bd2fe0e763b5b0 [file] [log] [blame] [edit]
// 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;
}