// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromecast/crash/linux/crash_testing_utils.h"
#include <utility>
#include "base/files/file_util.h"
#include "base/json/json_file_value_serializer.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/strings/string_split.h"
#include "base/values.h"
#include "chromecast/base/path_utils.h"
#include "chromecast/crash/linux/dump_info.h"
#define RCHECK(cond, retval, err) \
do { \
if (!(cond)) { \
LOG(ERROR) << (err); \
return (retval); \
} \
} while (0)
namespace chromecast {
namespace {
const char kRatelimitKey[] = "ratelimit";
const char kRatelimitPeriodStartKey[] = "period_start";
const char kRatelimitPeriodDumpsKey[] = "period_dumps";
std::optional<base::Value::List> ParseLockFile(const std::string& path) {
std::string lockfile_string;
RCHECK(base::ReadFileToString(base::FilePath(path), &lockfile_string),
std::nullopt, "Failed to read file");
std::vector<std::string> lines = base::SplitString(
lockfile_string, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
base::Value::List dumps;
// Validate dumps
for (const std::string& line : lines) {
if (line.size() == 0)
continue;
std::optional<base::Value> dump_info = base::JSONReader::Read(line);
RCHECK(dump_info.has_value(), std::nullopt, "Invalid DumpInfo");
DumpInfo info(&dump_info.value());
RCHECK(info.valid(), std::nullopt, "Invalid DumpInfo");
dumps.Append(std::move(dump_info.value()));
}
return dumps;
}
std::unique_ptr<base::Value> ParseMetadataFile(const std::string& path) {
base::FilePath file_path(path);
JSONFileValueDeserializer deserializer(file_path);
int error_code = -1;
std::string error_msg;
std::unique_ptr<base::Value> value =
deserializer.Deserialize(&error_code, &error_msg);
DLOG_IF(ERROR, !value) << "JSON error " << error_code << ":" << error_msg;
return value;
}
int WriteLockFile(const std::string& path, const base::Value::List& contents) {
std::string lockfile;
for (const auto& elem : contents) {
std::string dump_info;
bool ret = base::JSONWriter::Write(elem, &dump_info);
RCHECK(ret, -1, "Failed to serialize DumpInfo");
lockfile += dump_info;
lockfile += "\n"; // Add line seperatators
}
return WriteFile(base::FilePath(path), lockfile.c_str(), lockfile.size()) >= 0
? 0
: -1;
}
bool WriteMetadataFile(const std::string& path,
const base::Value::Dict& metadata) {
base::FilePath file_path(path);
JSONFileValueSerializer serializer(file_path);
return serializer.Serialize(metadata);
}
} // namespace
std::unique_ptr<DumpInfo> CreateDumpInfo(const std::string& json_string) {
std::optional<base::Value> value = base::JSONReader::Read(json_string);
return value.has_value() ? std::make_unique<DumpInfo>(&value.value())
: std::make_unique<DumpInfo>(nullptr);
}
bool FetchDumps(const std::string& lockfile_path,
std::vector<std::unique_ptr<DumpInfo>>* dumps) {
DCHECK(dumps);
std::optional<base::Value::List> dump_list = ParseLockFile(lockfile_path);
RCHECK(dump_list, false, "Failed to parse lockfile");
dumps->clear();
for (const auto& elem : *dump_list) {
std::unique_ptr<DumpInfo> dump(new DumpInfo(&elem));
RCHECK(dump->valid(), false, "Invalid DumpInfo");
dumps->push_back(std::move(dump));
}
return true;
}
bool ClearDumps(const std::string& lockfile_path) {
base::Value::List dump_list;
return WriteLockFile(lockfile_path, dump_list) == 0;
}
bool CreateFiles(const std::string& lockfile_path,
const std::string& metadata_path) {
base::Value::Dict metadata;
base::Value::Dict ratelimit_fields;
ratelimit_fields.Set(kRatelimitPeriodStartKey, 0.0);
ratelimit_fields.Set(kRatelimitPeriodDumpsKey, 0);
metadata.Set(kRatelimitKey, std::move(ratelimit_fields));
base::Value::List dumps;
return WriteLockFile(lockfile_path, dumps) == 0 &&
WriteMetadataFile(metadata_path, metadata);
}
bool AppendLockFile(const std::string& lockfile_path,
const std::string& metadata_path,
const DumpInfo& dump) {
std::optional<base::Value::List> contents = ParseLockFile(lockfile_path);
if (!contents) {
CreateFiles(lockfile_path, metadata_path);
if (!(contents = ParseLockFile(lockfile_path))) {
return false;
}
}
contents->Append(dump.GetAsValue());
return WriteLockFile(lockfile_path, *contents) == 0;
}
bool SetRatelimitPeriodStart(const std::string& metadata_path,
const base::Time& start) {
std::unique_ptr<base::Value> contents = ParseMetadataFile(metadata_path);
if (!contents || !contents->is_dict())
return false;
base::Value::Dict* ratelimit_params =
contents->GetDict().FindDict(kRatelimitKey);
if (!ratelimit_params)
return false;
ratelimit_params->Set(kRatelimitPeriodStartKey,
start.InSecondsFSinceUnixEpoch());
return WriteMetadataFile(metadata_path, contents->GetDict()) == 0;
}
} // namespace chromecast