blob: 8f028afe8b9778a7f17defdc80287be8c41ef2b0 [file] [log] [blame]
// Copyright 2020 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 "croslog/boot_records.h"
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/strings/string_number_conversions.h>
#include "croslog/log_line_reader.h"
#include "croslog/log_parser_syslog.h"
namespace croslog {
namespace {
// The maximum length of the boot log. The log must be less than 1000 lines,
// since it is trancated to 500 lines in log-bootid-on-boot.conf.
constexpr size_t kBootEntryMaxLen = 1000;
std::vector<BootRecords::BootEntry> ReadBootLogs(base::FilePath file_path) {
LogLineReader reader(LogLineReader::Backend::FILE);
std::vector<BootRecords::BootEntry> boot_log_entries;
if (!base::PathExists(file_path))
return boot_log_entries;
LogParserSyslog parser;
reader.OpenFile(std::move(file_path));
while (true) {
base::Optional<std::string> log = reader.Forward();
if (!log.has_value()) {
// EOF: finishes the read.
break;
}
MaybeLogEntry e = parser.Parse(std::move(*log));
if (!e.has_value()) {
// Parse error: continuing the next line.
continue;
}
if (!BootRecords::IsValidBootId(e->message())) {
continue;
}
boot_log_entries.emplace_back(e->time(), e->message());
}
return boot_log_entries;
}
std::vector<BootRecords::BootRange> ConvertBootEntriesToRanges(
const std::vector<BootRecords::BootEntry>& boot_log_entries) {
std::vector<BootRecords::BootRange> boot_log_ranges;
for (int i = 0; i < boot_log_entries.size(); i++) {
const auto& boot_entry = boot_log_entries[i];
base::Time next_boot_time = (i < (boot_log_entries.size() - 1))
? boot_log_entries[i + 1].boot_time()
: base::Time::Max();
// Boot times should be in an increasing order.
if (boot_entry.boot_time() >= next_boot_time) {
LOG(WARNING) << "Boot entries must be in an incremental order, but not: "
<< boot_entry.boot_time() << " -> " << next_boot_time
<< ". This "
<< "entry is ignored.";
continue;
}
boot_log_ranges.emplace_back(boot_entry.boot_time(), next_boot_time,
boot_entry.boot_id());
}
return boot_log_ranges;
}
std::vector<BootRecords::BootRange> ReadBootRecords(base::FilePath file_path) {
return ConvertBootEntriesToRanges(ReadBootLogs(file_path));
}
} // anonymous namespace
// ============================================================================
// BootRecords::BootEntry implementation:
BootRecords::BootEntry::BootEntry(base::Time boot_time, std::string boot_id)
: boot_time_(boot_time), boot_id_(std::move(boot_id)) {}
// ============================================================================
// BootRecords::BootRange implementation:
BootRecords::BootRange::BootRange(base::Time boot_time,
base::Time next_boot_time,
std::string boot_id)
: boot_time_(boot_time),
next_boot_time_(next_boot_time),
boot_id_(std::move(boot_id)) {}
bool BootRecords::BootRange::Contains(base::Time time) const {
return boot_time_ <= time && time < next_boot_time_;
}
bool operator==(BootRecords::BootRange const& a,
BootRecords::BootRange const& b) {
return a.boot_id() == b.boot_id() && a.boot_time() == b.boot_time() &&
a.next_boot_time() == b.next_boot_time();
}
// ============================================================================
// BootRecords implementation:
// static
bool BootRecords::IsValidBootId(const std::string& boot_id) {
if (boot_id.size() != 32)
return false;
for (int i = 0; i < 32; i++) {
if (!(boot_id[i] >= '0' && boot_id[i] <= '9') &&
!(boot_id[i] >= 'a' && boot_id[i] <= 'f')) {
return false;
}
}
return true;
}
BootRecords::BootRecords()
: BootRecords(base::FilePath("/var/log/boot_id.log")) {}
BootRecords::BootRecords(base::FilePath file_path)
: boot_ranges_(ReadBootRecords(file_path)) {
DCHECK_GT(kBootEntryMaxLen, boot_ranges_.size());
}
BootRecords::BootRecords(std::vector<BootRecords::BootEntry> entries)
: boot_ranges_(ConvertBootEntriesToRanges(entries)) {}
base::Optional<BootRecords::BootRange> BootRecords::GetBootRange(
const std::string& boot_str) const {
int boot_offset = 0;
if (boot_str.empty() || base::StringToInt(boot_str, &boot_offset)) {
if (boot_str.empty())
boot_offset = 0;
// The specified string may be a boot number.
DCHECK_GT(kBootEntryMaxLen, boot_ranges_.size());
int boot_offset_nth;
if (boot_offset <= 0) {
boot_offset_nth = boot_ranges_.size() + boot_offset - 1;
if (boot_offset_nth < 0) {
// Invalid offset.
return base::nullopt;
}
} else {
// Positive offset is not supported.
// TODO(yoshiki): support positive offset values.
return base::nullopt;
}
return boot_ranges_[boot_offset_nth];
}
if (IsValidBootId(boot_str)) {
// The specified string may be a boot ID.
for (int i = 0; i < boot_ranges_.size(); i++) {
const auto& boot_entry = boot_ranges_[i];
if (boot_entry.boot_id() != boot_str)
continue;
return boot_entry;
}
}
// Invalid boot ID format, or no corresponding boot in the entries.
return base::nullopt;
}
} // namespace croslog