blob: c808b43b4859c1bb5d0fd7485b8934e5cfc2d1eb [file] [log] [blame]
/*
* Copyright 2021 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 "common/reloadable_config_file.h"
#include <iomanip>
#include <optional>
#include <string>
#include <utility>
#include <base/files/file_util.h>
#include <base/json/json_reader.h>
#include <base/json/json_writer.h>
#include "cros-camera/common.h"
namespace cros {
ReloadableConfigFile::ReloadableConfigFile(const Options& options)
: default_config_file_path_(options.default_config_file_path),
override_config_file_path_(options.override_config_file_path) {
base::AutoLock lock(options_lock_);
ReadConfigFileLocked(default_config_file_path_);
if (!override_config_file_path_.empty()) {
ReadConfigFileLocked(override_config_file_path_);
bool ret = override_file_path_watcher_.Watch(
override_config_file_path_, base::FilePathWatcher::Type::kNonRecursive,
base::BindRepeating(&ReloadableConfigFile::OnConfigFileUpdated,
base::Unretained(this)));
DCHECK(ret) << "Can't monitor override config file path: "
<< override_config_file_path_;
}
}
void ReloadableConfigFile::SetCallback(OptionsUpdateCallback callback) {
options_update_callback_ = std::move(callback);
base::AutoLock lock(options_lock_);
if (!json_values_.is_none()) {
options_update_callback_.Run(json_values_);
}
}
void ReloadableConfigFile::UpdateOption(std::string key, base::Value value) {
base::AutoLock lock(options_lock_);
json_values_.SetKey(key, std::move(value));
WriteConfigFileLocked(override_config_file_path_);
}
bool ReloadableConfigFile::IsValid() const {
return !json_values_.is_none();
}
void ReloadableConfigFile::ReadConfigFileLocked(
const base::FilePath& file_path) {
options_lock_.AssertAcquired();
if (file_path.empty() || !base::PathExists(file_path)) {
return;
}
// Limiting config file size to 64KB. Increase this if needed.
constexpr size_t kConfigFileMaxSize = 65536;
std::string contents;
CHECK(base::ReadFileToStringWithMaxSize(file_path, &contents,
kConfigFileMaxSize));
std::optional<base::Value> json_values =
base::JSONReader::Read(contents, base::JSON_ALLOW_TRAILING_COMMAS);
if (!json_values) {
LOGF(ERROR) << "Failed to load the config file content of " << file_path;
return;
}
if (json_values_.is_dict() && json_values->is_dict()) {
// Merge the new and existing config if both are dictionary. Keys that are
// present both in the existing and new config will be overwritten with the
// new value.
json_values_.MergeDictionary(&json_values.value());
} else {
json_values_ = std::move(*json_values);
}
}
void ReloadableConfigFile::WriteConfigFileLocked(
const base::FilePath& file_path) {
options_lock_.AssertAcquired();
std::string json_string;
if (!base::JSONWriter::WriteWithOptions(
json_values_, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_string)) {
LOGF(WARNING) << "Can't jsonify config settings";
return;
}
if (!base::WriteFile(file_path, json_string)) {
LOGF(WARNING) << "Can't write config settings to "
<< std::quoted(file_path.value());
}
}
void ReloadableConfigFile::OnConfigFileUpdated(const base::FilePath& file_path,
bool error) {
base::AutoLock lock(options_lock_);
ReadConfigFileLocked(override_config_file_path_);
if (options_update_callback_) {
options_update_callback_.Run(json_values_.Clone());
}
}
bool LoadIfExist(const base::Value& json_values,
const char* key,
float* output) {
if (!output) {
LOGF(ERROR) << "output cannot be nullptr";
return false;
}
auto value = json_values.FindDoubleKey(key);
if (!value) {
return false;
}
*output = *value;
return true;
}
bool LoadIfExist(const base::Value& json_values, const char* key, int* output) {
if (!output) {
LOGF(ERROR) << "output cannot be nullptr";
return false;
}
auto value = json_values.FindIntKey(key);
if (!value) {
return false;
}
*output = *value;
return true;
}
bool LoadIfExist(const base::Value& json_values,
const char* key,
bool* output) {
if (!output) {
LOGF(ERROR) << "output cannot be nullptr";
return false;
}
auto value = json_values.FindBoolKey(key);
if (!value) {
return false;
}
*output = *value;
return true;
}
} // namespace cros