blob: e50aff5f56cdc7bf206660e74cdab67056744e71 [file] [log] [blame]
// Copyright 2019 The Chromium OS 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 "diagnostics/wilco_dtc_supportd/telemetry/system_files_service_impl.h"
#include <utility>
#include <base/files/file_enumerator.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/string_util.h>
namespace diagnostics {
// static
base::FilePath SystemFilesServiceImpl::GetPathForFile(
SystemFilesService::File location) {
switch (location) {
case File::kProcUptime:
return base::FilePath("proc/uptime");
case File::kProcMeminfo:
return base::FilePath("proc/meminfo");
case File::kProcLoadavg:
return base::FilePath("proc/loadavg");
case File::kProcStat:
return base::FilePath("proc/stat");
case File::kProcNetNetstat:
return base::FilePath("proc/net/netstat");
case File::kProcNetDev:
return base::FilePath("proc/net/dev");
case File::kProcDiskstats:
return base::FilePath("proc/diskstats");
case File::kProcCpuinfo:
return base::FilePath("proc/cpuinfo");
case File::kProcVmstat:
return base::FilePath("proc/vmstat");
}
NOTREACHED();
}
// static
base::FilePath SystemFilesServiceImpl::GetPathForDirectory(
SystemFilesService::Directory location) {
switch (location) {
case Directory::kProcAcpiButton:
return base::FilePath("proc/acpi/button/");
case Directory::kSysClassHwmon:
return base::FilePath("sys/class/hwmon/");
case Directory::kSysClassThermal:
return base::FilePath("sys/class/thermal/");
case Directory::kSysFirmwareDmiTables:
return base::FilePath("sys/firmware/dmi/tables/");
case Directory::kSysClassPowerSupply:
return base::FilePath("sys/class/power_supply/");
case Directory::kSysClassBacklight:
return base::FilePath("sys/class/backlight/");
case Directory::kSysClassNetwork:
return base::FilePath("sys/class/net/");
case Directory::kSysDevicesSystemCpu:
return base::FilePath("sys/devices/system/cpu/");
}
NOTREACHED();
}
// static
base::FilePath SystemFilesServiceImpl::GetPathForVpdField(VpdField vpd_field) {
switch (vpd_field) {
case VpdField::kActivateDate:
return base::FilePath("run/wilco_dtc/vpd_fields/ActivateDate");
case VpdField::kAssetId:
return base::FilePath("run/wilco_dtc/vpd_fields/asset_id");
case VpdField::kMfgDate:
return base::FilePath("run/wilco_dtc/vpd_fields/mfg_date");
case VpdField::kModelName:
return base::FilePath("run/wilco_dtc/vpd_fields/model_name");
case VpdField::kSerialNumber:
return base::FilePath("run/wilco_dtc/vpd_fields/serial_number");
case VpdField::kSkuNumber:
return base::FilePath("run/wilco_dtc/vpd_fields/sku_number");
case VpdField::kSystemId:
return base::FilePath("run/wilco_dtc/vpd_fields/system_id");
case VpdField::kUuid:
return base::FilePath("run/wilco_dtc/vpd_fields/uuid_id");
}
NOTREACHED();
}
SystemFilesServiceImpl::SystemFilesServiceImpl() = default;
SystemFilesServiceImpl::~SystemFilesServiceImpl() = default;
base::Optional<SystemFilesService::FileDump>
SystemFilesServiceImpl::GetFileDump(File location) {
FileDump dump;
if (!MakeFileDump(root_dir_.Append(GetPathForFile(location)), &dump)) {
return base::nullopt;
}
return std::move(dump);
}
base::Optional<SystemFilesService::FileDumps>
SystemFilesServiceImpl::GetDirectoryDump(Directory location) {
base::FilePath path = root_dir_.Append(GetPathForDirectory(location));
if (!base::DirectoryExists(path))
return base::nullopt;
FileDumps dumps;
std::set<std::string> visited_paths;
SearchDirectory(path, &visited_paths, &dumps);
return std::move(dumps);
}
base::Optional<std::string> SystemFilesServiceImpl::GetVpdField(
VpdField vpd_field) {
FileDump dump;
if (!MakeFileDump(root_dir_.Append(GetPathForVpdField(vpd_field)), &dump)) {
return base::nullopt;
}
base::TrimString(dump.contents, base::kWhitespaceASCII, &dump.contents);
if (dump.contents.empty() || !base::IsStringASCII(dump.contents)) {
VLOG(2) << "VPD field from " << GetPathForVpdField(vpd_field).BaseName()
<< " is not non-empty ASCII string";
return base::nullopt;
}
return std::move(dump.contents);
}
void SystemFilesServiceImpl::set_root_dir_for_testing(
const base::FilePath& dir) {
root_dir_ = dir;
}
bool SystemFilesServiceImpl::MakeFileDump(const base::FilePath& file_path,
FileDump* file_dump) const {
std::string file_contents;
if (!base::ReadFileToString(file_path, &file_contents)) {
VPLOG(2) << "Failed to read from " << file_path.value();
return false;
}
const base::FilePath canonical_file_path =
base::MakeAbsoluteFilePath(file_path);
if (canonical_file_path.empty()) {
PLOG(ERROR) << "Failed to obtain canonical path for " << file_path.value();
return false;
}
VLOG(3) << "Read " << file_contents.size() << " bytes from "
<< file_path.value() << " with canonical path "
<< canonical_file_path.value();
file_dump->path = file_path;
file_dump->canonical_path = canonical_file_path;
file_dump->contents = std::move(file_contents);
return true;
}
void SystemFilesServiceImpl::SearchDirectory(
const base::FilePath& root_dir,
std::set<std::string>* visited_paths,
FileDumps* file_dumps) const {
visited_paths->insert(base::MakeAbsoluteFilePath(root_dir).value());
base::FileEnumerator file_enum(
base::FilePath(root_dir), false,
base::FileEnumerator::FileType::FILES |
base::FileEnumerator::FileType::DIRECTORIES |
base::FileEnumerator::FileType::SHOW_SYM_LINKS);
for (base::FilePath path = file_enum.Next(); !path.empty();
path = file_enum.Next()) {
// Only certain symlinks are followed - see the comments for
// ShouldFollowSymlink for a full description of the behavior.
if (base::IsLink(path) && !ShouldFollowSymlink(path))
continue;
base::FilePath canonical_path = base::MakeAbsoluteFilePath(path);
if (canonical_path.empty()) {
VPLOG(2) << "Failed to resolve path '" << path.value() << "'.";
continue;
}
// Prevent visiting duplicate paths, which could happen due to following
// symlinks.
if (visited_paths->find(canonical_path.value()) != visited_paths->end())
continue;
visited_paths->insert(canonical_path.value());
if (base::DirectoryExists(path)) {
SearchDirectory(path, visited_paths, file_dumps);
} else {
auto file_dump = std::make_unique<FileDump>();
if (!MakeFileDump(path, file_dump.get())) {
// When a file is failed to be dumped, it's just omitted from the
// returned list of entries.
continue;
}
file_dumps->push_back(std::move(file_dump));
}
}
}
bool SystemFilesServiceImpl::ShouldFollowSymlink(
const base::FilePath& link) const {
// Path relative to the root directory where we will follow symlinks.
constexpr char kAllowableSymlinkParentDir[] = "sys/class";
return base::FilePath(root_dir_.Append(kAllowableSymlinkParentDir)) ==
link.DirName().DirName();
}
} // namespace diagnostics