blob: 9b4d06272898ff03d5b0622f8eb060b9d8371e03 [file] [log] [blame]
// Copyright 2019 The Fuchsia 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 "src/modular/lib/modular_config/modular_config.h"
#include <fcntl.h>
#include <lib/syslog/cpp/macros.h>
#include <rapidjson/document.h>
#include <rapidjson/error/en.h>
#include "src/lib/files/directory.h"
#include "src/lib/files/file.h"
#include "src/lib/files/path.h"
#include "src/lib/fxl/strings/substitute.h"
#include "src/modular/lib/fidl/clone.h"
#include "src/modular/lib/fidl/json_xdr.h"
#include "src/modular/lib/modular_config/modular_config_constants.h"
#include "src/modular/lib/modular_config/modular_config_xdr.h"
// Flags passed to RapidJSON that control JSON parsing behavior.
// This is used to enable parsing non-standard JSON syntax, like comments.
constexpr unsigned kModularConfigParseFlags = rapidjson::kParseCommentsFlag;
namespace modular {
namespace {
rapidjson::Document GetSectionAsDoc(const rapidjson::Document& doc,
const std::string& section_name) {
rapidjson::Document section_doc;
auto config_json = doc.FindMember(section_name);
if (config_json != doc.MemberEnd()) {
section_doc.CopyFrom(config_json->value, section_doc.GetAllocator());
} else {
// |section_name| was not found; return an empty object
section_doc.SetObject();
}
return section_doc;
}
std::string StripLeadingSlash(std::string str) {
if (str.find("/") == 0)
return str.substr(1);
return str;
}
} // namespace
fit::result<fuchsia::modular::session::ModularConfig, std::string> ParseConfig(
std::string_view config_json) {
rapidjson::Document doc;
doc.Parse<kModularConfigParseFlags>(config_json.data(), config_json.length());
if (doc.HasParseError()) {
auto error = std::stringstream();
error << "Failed to parse JSON: " << rapidjson::GetParseError_En(doc.GetParseError()) << " ("
<< doc.GetErrorOffset() << ")";
return fit::error(error.str());
}
fuchsia::modular::session::ModularConfig config;
if (!XdrRead(&doc, &config, XdrModularConfig)) {
return fit::error("Failed to read JSON as Modular configuration (does not follow schema?)");
}
return fit::ok(std::move(config));
}
// Returns the default Modular configuration.
fuchsia::modular::session::ModularConfig DefaultConfig() {
rapidjson::Document doc;
doc.SetObject();
fuchsia::modular::session::ModularConfig config;
auto ok = XdrRead(&doc, &config, XdrModularConfig);
FX_DCHECK(ok);
return config;
}
std::string ConfigToJsonString(const fuchsia::modular::session::ModularConfig& config) {
std::string json;
auto config_copy = CloneStruct(config);
XdrWrite(&json, &config_copy, XdrModularConfig);
return json;
}
ModularConfigReader::ModularConfigReader(fbl::unique_fd dir_fd) {
FX_CHECK(dir_fd.get() >= 0);
// 1. Figure out where the config file is.
std::string config_path = files::JoinPath(StripLeadingSlash(modular_config::kOverriddenConfigDir),
modular_config::kStartupConfigFilePath);
if (!files::IsFileAt(dir_fd.get(), config_path)) {
config_path = files::JoinPath(StripLeadingSlash(modular_config::kDefaultConfigDir),
modular_config::kStartupConfigFilePath);
}
// 2. Read the file
std::string config;
if (!files::ReadFileToStringAt(dir_fd.get(), config_path, &config)) {
FX_LOGS(ERROR) << "Failed to read file: " << config_path;
UseDefaults();
return;
}
// 3. Parse the JSON
ParseConfig(config, config_path);
}
ModularConfigReader::ModularConfigReader(std::string config) {
constexpr char kPathForErrorStrings[] = ".";
ParseConfig(config, kPathForErrorStrings);
}
// static
ModularConfigReader ModularConfigReader::CreateFromNamespace() {
return ModularConfigReader(fbl::unique_fd(open("/", O_RDONLY)));
}
// static
std::string ModularConfigReader::GetOverriddenConfigPath() {
return files::JoinPath(StripLeadingSlash(modular_config::kOverriddenConfigDir),
modular_config::kStartupConfigFilePath);
}
// static
bool ModularConfigReader::OverriddenConfigExists() {
return files::IsFile(GetOverriddenConfigPath());
}
void ModularConfigReader::ParseConfig(const std::string& config, const std::string& config_path) {
rapidjson::Document doc;
doc.Parse<kModularConfigParseFlags>(config);
if (doc.HasParseError()) {
FX_LOGS(ERROR) << "Failed to parse " << config_path << ": "
<< rapidjson::GetParseError_En(doc.GetParseError()) << " ("
<< doc.GetErrorOffset() << ")";
UseDefaults();
return;
}
// Parse the `basemgr` and `sessionmgr` sections out of the config.
rapidjson::Document basemgr_doc = GetSectionAsDoc(doc, modular_config::kBasemgrConfigName);
rapidjson::Document sessionmgr_doc = GetSectionAsDoc(doc, modular_config::kSessionmgrConfigName);
if (!XdrRead(&basemgr_doc, &basemgr_config_, XdrBasemgrConfig)) {
FX_LOGS(ERROR) << "Unable to parse 'basemgr' from " << config_path;
}
if (!XdrRead(&sessionmgr_doc, &sessionmgr_config_, XdrSessionmgrConfig)) {
FX_LOGS(ERROR) << "Unable to parse 'sessionmgr' from " << config_path;
}
}
void ModularConfigReader::UseDefaults() {
rapidjson::Document doc;
doc.SetObject();
XdrRead(&doc, &basemgr_config_, XdrBasemgrConfig);
XdrRead(&doc, &sessionmgr_config_, XdrSessionmgrConfig);
}
fuchsia::modular::session::BasemgrConfig ModularConfigReader::GetBasemgrConfig() const {
fuchsia::modular::session::BasemgrConfig result;
basemgr_config_.Clone(&result);
return result;
}
fuchsia::modular::session::SessionmgrConfig ModularConfigReader::GetSessionmgrConfig() const {
fuchsia::modular::session::SessionmgrConfig result;
sessionmgr_config_.Clone(&result);
return result;
}
// static
std::string ModularConfigReader::GetConfigAsString(
fuchsia::modular::session::BasemgrConfig* basemgr_config,
fuchsia::modular::session::SessionmgrConfig* sessionmgr_config) {
std::string basemgr_json;
std::string sessionmgr_json;
XdrWrite(&basemgr_json, basemgr_config, XdrBasemgrConfig);
XdrWrite(&sessionmgr_json, sessionmgr_config, XdrSessionmgrConfig);
return fxl::Substitute(R"({
"$0": $1,
"$2": $3
})",
modular_config::kBasemgrConfigName, basemgr_json,
modular_config::kSessionmgrConfigName, sessionmgr_json);
}
} // namespace modular