blob: 1be7aff3fd1156f7bb6b8105d52a18af063a6fe9 [file] [log] [blame]
// Copyright 2016 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.
// Libary to provide access to the Chrome OS master configuration in YAML / JSON
// format
#include "chromeos-config/libcros_config/cros_config_json.h"
#include <string>
#include <utility>
#include <vector>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/json/json_reader.h>
#include <base/logging.h>
#include <base/optional.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include "chromeos-config/libcros_config/cros_config.h"
#include "chromeos-config/libcros_config/identity.h"
#include "chromeos-config/libcros_config/identity_arm.h"
#include "chromeos-config/libcros_config/identity_x86.h"
namespace brillo {
constexpr char CrosConfigJson::kRootName[];
constexpr char CrosConfigJson::kConfigListName[];
CrosConfigJson::CrosConfigJson() : config_dict_(nullptr) {}
CrosConfigJson::~CrosConfigJson() {}
bool CrosConfigJson::SelectConfigByIdentityInternal(
const CrosConfigIdentity& identity) {
if (!json_config_.is_dict())
return false;
const base::Value* chromeos = json_config_.FindDictKey(kRootName);
if (!chromeos)
return false;
const base::Value* configs_list = chromeos->FindListKey(kConfigListName);
if (!configs_list)
return false;
const std::string& find_whitelabel_name = identity.GetVpdId();
const int find_sku_id = identity.GetSkuId();
for (size_t i = 0; i < configs_list->GetList().size(); ++i) {
const base::Value& config_dict = configs_list->GetList()[i];
if (!config_dict.is_dict())
continue;
const base::Value* identity_dict = config_dict.FindDictKey("identity");
if (!identity_dict)
continue;
// Check SMBIOS name matches (x86) or dt-compatible (arm)
if (!identity.PlatformIdentityMatch(*identity_dict))
continue;
// Check that either the SKU is less than zero, or the current
// entry has a matching SKU id. If sku-id is not defined in the
// identity dictionary, this entry will match any SKU id.
if (find_sku_id != kDefaultSkuId) {
base::Optional<int> current_sku_id = identity_dict->FindIntKey("sku-id");
if (current_sku_id && current_sku_id != find_sku_id)
continue;
}
// Currently, the find_whitelabel_name can be either the
// whitelabel-tag or the customization-id.
const std::string* current_vpd_tag =
identity_dict->FindStringKey("whitelabel-tag");
if (!current_vpd_tag)
current_vpd_tag = identity_dict->FindStringKey("customization-id");
if (!current_vpd_tag)
current_vpd_tag = &base::EmptyString();
if (*current_vpd_tag != find_whitelabel_name)
continue;
// SMBIOS name matches/dt-compatible, SKU matches, and VPD tag
// matches. This is the config.
config_dict_ = &config_dict;
device_index_ = i;
return true;
}
return false;
}
bool CrosConfigJson::SelectConfigByIdentity(
const CrosConfigIdentity& identity) {
if (!SelectConfigByIdentityInternal(identity)) {
CROS_CONFIG_LOG(ERROR) << "Failed to find config for "
<< identity.DebugString();
return false;
}
inited_ = true;
return true;
}
bool CrosConfigJson::GetString(const std::string& path,
const std::string& property,
std::string* val_out) {
if (!InitCheck()) {
return false;
}
if (path.empty()) {
LOG(ERROR) << "Path must be specified";
return false;
}
if (path[0] != '/') {
LOG(ERROR) << "Path must start with / specifying the root node";
return false;
}
const base::Value* attr_dict = config_dict_;
if (path.length() > 1) {
std::string path_no_root = path.substr(1);
for (const auto& path : base::SplitStringPiece(
path_no_root, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
attr_dict = attr_dict->FindDictKey(path);
if (!attr_dict) {
CROS_CONFIG_LOG(ERROR) << "Failed to find path: " << path;
return false;
}
}
}
const base::Value* value = attr_dict->FindKey(property);
if (!value)
return false;
switch (value->type()) {
case base::Value::Type::STRING:
val_out->assign(value->GetString());
return true;
case base::Value::Type::INTEGER:
val_out->assign(std::to_string(value->GetInt()));
return true;
case base::Value::Type::BOOLEAN:
val_out->assign(value->GetBool() ? "true" : "false");
return true;
default:
return false;
}
}
bool CrosConfigJson::GetDeviceIndex(int* device_index_out) {
if (!InitCheck()) {
return false;
}
*device_index_out = device_index_;
return true;
}
bool CrosConfigJson::ReadConfigFile(const base::FilePath& filepath) {
std::string json_data;
if (!base::ReadFileToString(filepath, &json_data)) {
CROS_CONFIG_LOG(ERROR) << "Could not read file " << filepath.MaybeAsASCII();
return false;
}
auto json_root = base::JSONReader::ReadAndReturnValueWithError(
json_data, base::JSON_PARSE_RFC);
if (!json_root.value) {
CROS_CONFIG_LOG(ERROR) << "Fail to parse config.json: "
<< json_root.error_message;
return false;
}
json_config_ = std::move(json_root.value.value());
return true;
}
} // namespace brillo