2018-07-10 02:53:17 +03:00
|
|
|
/*
|
2019-06-20 02:58:25 +03:00
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
2018-07-10 02:53:17 +03:00
|
|
|
*
|
2019-06-20 02:58:25 +03:00
|
|
|
* This software may be used and distributed according to the terms of the
|
|
|
|
* GNU General Public License version 2.
|
2018-07-10 02:53:17 +03:00
|
|
|
*/
|
2019-05-08 03:36:45 +03:00
|
|
|
#include "eden/fs/config/EdenConfig.h"
|
|
|
|
|
2019-03-07 02:16:30 +03:00
|
|
|
#include <cpptoml.h> // @manual=fbsource//third-party/cpptoml:cpptoml
|
2019-05-08 03:36:45 +03:00
|
|
|
#include <array>
|
|
|
|
|
2018-07-10 02:53:17 +03:00
|
|
|
#include <folly/File.h>
|
|
|
|
#include <folly/FileUtil.h>
|
|
|
|
#include <folly/Range.h>
|
|
|
|
#include <folly/String.h>
|
|
|
|
#include <folly/io/Cursor.h>
|
|
|
|
#include <folly/json.h>
|
|
|
|
#include <folly/logging/xlog.h>
|
2019-05-08 03:36:45 +03:00
|
|
|
|
|
|
|
#include "eden/fs/config/FileChangeMonitor.h"
|
2018-07-10 02:53:17 +03:00
|
|
|
|
2019-05-10 02:28:23 +03:00
|
|
|
#ifdef _WIN32
|
2019-05-10 02:28:23 +03:00
|
|
|
#include "eden/fs/win/utils/Stub.h" // @manual
|
2018-10-21 03:33:33 +03:00
|
|
|
#endif
|
|
|
|
|
2018-07-10 02:53:17 +03:00
|
|
|
using folly::StringPiece;
|
2018-10-24 03:03:12 +03:00
|
|
|
using std::optional;
|
2018-07-10 02:53:17 +03:00
|
|
|
using std::string;
|
|
|
|
|
|
|
|
const facebook::eden::RelativePathPiece kDefaultEdenDirectory{".eden"};
|
2019-02-08 05:28:16 +03:00
|
|
|
const facebook::eden::RelativePathPiece kDefaultUserIgnoreFile{".edenignore"};
|
|
|
|
const facebook::eden::RelativePathPiece kDefaultSystemIgnoreFile{"ignore"};
|
2018-07-10 02:53:17 +03:00
|
|
|
const facebook::eden::AbsolutePath kUnspecifiedDefault{"/"};
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
template <typename String>
|
|
|
|
void toAppend(facebook::eden::EdenConfig& ec, String* result) {
|
|
|
|
folly::toAppend(ec.toString(), result);
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace facebook {
|
|
|
|
namespace eden {
|
|
|
|
|
|
|
|
std::string EdenConfig::toString(facebook::eden::ConfigSource cs) const {
|
|
|
|
switch (cs) {
|
2019-06-05 21:47:01 +03:00
|
|
|
case ConfigSource::Default:
|
2018-07-10 02:53:17 +03:00
|
|
|
return "default";
|
2019-06-05 21:47:01 +03:00
|
|
|
case ConfigSource::CommandLine:
|
2018-07-10 02:53:17 +03:00
|
|
|
return "command-line";
|
2019-06-05 21:47:01 +03:00
|
|
|
case ConfigSource::UserConfig:
|
2018-07-10 02:53:17 +03:00
|
|
|
return userConfigPath_.c_str();
|
2019-06-05 21:47:01 +03:00
|
|
|
case ConfigSource::SystemConfig:
|
2018-07-10 02:53:17 +03:00
|
|
|
return systemConfigPath_.c_str();
|
|
|
|
}
|
|
|
|
throw std::invalid_argument(
|
|
|
|
folly::to<string>("invalid config source value: ", static_cast<int>(cs)));
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string EdenConfig::toString() const {
|
|
|
|
std::string rslt;
|
|
|
|
rslt += folly::to<std::string>(
|
|
|
|
"[ EdenConfig settings ]\n",
|
|
|
|
"userConfigPath=",
|
|
|
|
userConfigPath_,
|
|
|
|
"\n"
|
|
|
|
"systemConfigDir=",
|
|
|
|
systemConfigDir_,
|
|
|
|
"\n"
|
|
|
|
"systemConfigPath=",
|
|
|
|
systemConfigPath_,
|
|
|
|
"\n");
|
|
|
|
|
|
|
|
rslt += "[ EdenConfig values ]\n";
|
|
|
|
for (const auto& sectionEntry : configMap_) {
|
|
|
|
auto sectionKey = sectionEntry.first;
|
|
|
|
for (const auto& keyEntry : sectionEntry.second) {
|
|
|
|
rslt += folly::to<std::string>(
|
|
|
|
sectionKey,
|
|
|
|
":",
|
|
|
|
keyEntry.first,
|
|
|
|
"=",
|
|
|
|
keyEntry.second->getStringValue(),
|
|
|
|
"\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rslt += "[ EdenConfig sources ]\n";
|
|
|
|
for (const auto& sectionEntry : configMap_) {
|
|
|
|
auto sectionKey = sectionEntry.first;
|
|
|
|
for (const auto& keyEntry : sectionEntry.second) {
|
|
|
|
rslt += folly::to<std::string>(
|
|
|
|
sectionKey,
|
|
|
|
":",
|
|
|
|
keyEntry.first,
|
|
|
|
"=",
|
|
|
|
EdenConfig::toString(keyEntry.second->getSource()),
|
|
|
|
"\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rslt += "]\n";
|
|
|
|
return rslt;
|
|
|
|
}
|
|
|
|
|
2019-06-11 22:59:03 +03:00
|
|
|
EdenConfigData EdenConfig::toThriftConfigData() const {
|
|
|
|
EdenConfigData result;
|
|
|
|
for (const auto& sectionEntry : configMap_) {
|
|
|
|
const auto& sectionKey = sectionEntry.first;
|
|
|
|
for (const auto& keyEntry : sectionEntry.second) {
|
|
|
|
auto keyName = folly::to<string>(sectionKey, ":", keyEntry.first);
|
|
|
|
auto& configValue = result.values[keyName];
|
|
|
|
configValue.parsedValue = keyEntry.second->getStringValue();
|
|
|
|
configValue.source = keyEntry.second->getSource();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-07-10 02:53:17 +03:00
|
|
|
EdenConfig::EdenConfig(
|
2018-08-06 23:49:33 +03:00
|
|
|
folly::StringPiece userName,
|
2019-03-15 04:13:29 +03:00
|
|
|
uid_t userID,
|
2018-07-10 02:53:17 +03:00
|
|
|
AbsolutePath userHomePath,
|
|
|
|
AbsolutePath userConfigPath,
|
|
|
|
AbsolutePath systemConfigDir,
|
|
|
|
AbsolutePath systemConfigPath)
|
2018-08-06 23:49:33 +03:00
|
|
|
: userName_(userName),
|
2019-03-15 04:13:29 +03:00
|
|
|
userID_(userID),
|
2018-08-06 23:49:33 +03:00
|
|
|
userHomePath_(userHomePath),
|
2018-07-10 02:53:17 +03:00
|
|
|
userConfigPath_(userConfigPath),
|
|
|
|
systemConfigPath_(systemConfigPath),
|
|
|
|
systemConfigDir_(systemConfigDir) {
|
|
|
|
// Force set defaults that require passed arguments
|
|
|
|
edenDir_.setValue(
|
2019-06-05 21:47:01 +03:00
|
|
|
userHomePath_ + kDefaultEdenDirectory, ConfigSource::Default, true);
|
2018-07-13 20:48:07 +03:00
|
|
|
userIgnoreFile_.setValue(
|
2019-06-05 21:47:01 +03:00
|
|
|
userHomePath + kDefaultUserIgnoreFile, ConfigSource::Default, true);
|
2018-07-10 02:53:17 +03:00
|
|
|
systemIgnoreFile_.setValue(
|
2019-06-05 21:47:01 +03:00
|
|
|
systemConfigDir_ + kDefaultSystemIgnoreFile, ConfigSource::Default, true);
|
|
|
|
clientCertificate_.setValue(kUnspecifiedDefault, ConfigSource::Default, true);
|
2018-07-10 02:53:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
EdenConfig::EdenConfig(const EdenConfig& source) {
|
|
|
|
doCopy(source);
|
|
|
|
}
|
|
|
|
|
2018-11-09 22:22:28 +03:00
|
|
|
AbsolutePathPiece EdenConfig::getUserHomePath() const {
|
|
|
|
return userHomePath_;
|
|
|
|
}
|
|
|
|
|
2019-05-09 02:11:49 +03:00
|
|
|
const std::string& EdenConfig::getUserName() const {
|
|
|
|
return userName_;
|
|
|
|
}
|
|
|
|
|
|
|
|
uid_t EdenConfig::getUserID() const {
|
|
|
|
return userID_;
|
|
|
|
}
|
|
|
|
|
2018-07-10 02:53:17 +03:00
|
|
|
EdenConfig& EdenConfig::operator=(const EdenConfig& source) {
|
|
|
|
doCopy(source);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EdenConfig::doCopy(const EdenConfig& source) {
|
2019-05-09 02:11:49 +03:00
|
|
|
userName_ = source.userName_;
|
|
|
|
userID_ = source.userID_;
|
2018-07-10 02:53:17 +03:00
|
|
|
userHomePath_ = source.userHomePath_;
|
|
|
|
userConfigPath_ = source.userConfigPath_;
|
|
|
|
systemConfigPath_ = source.systemConfigPath_;
|
|
|
|
systemConfigDir_ = source.systemConfigDir_;
|
|
|
|
|
|
|
|
// Copy each ConfigSettings from source.
|
|
|
|
for (const auto& sectionEntry : source.configMap_) {
|
|
|
|
auto& section = sectionEntry.first;
|
|
|
|
auto& keyMap = sectionEntry.second;
|
|
|
|
for (const auto& kvEntry : keyMap) {
|
|
|
|
// Here we are using the assignment operator to copy the configSetting.
|
|
|
|
// We are using the base pointer to do the assignment.
|
|
|
|
configMap_[section][kvEntry.first]->copyFrom(*kvEntry.second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EdenConfig::registerConfiguration(ConfigSettingBase* configSetting) {
|
|
|
|
StringPiece fullKeyStr = configSetting->getConfigKey();
|
|
|
|
auto pos = fullKeyStr.find(":");
|
|
|
|
if (pos != std::string::npos) {
|
|
|
|
StringPiece section(fullKeyStr.data(), pos);
|
|
|
|
StringPiece key(fullKeyStr.data() + pos + 1, fullKeyStr.end());
|
|
|
|
auto& keyMap = configMap_[section.str()];
|
|
|
|
keyMap[key.str()] = configSetting;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const AbsolutePath& EdenConfig::getEdenDir() const {
|
|
|
|
return edenDir_.getValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
const AbsolutePath& EdenConfig::getSystemIgnoreFile() const {
|
|
|
|
return systemIgnoreFile_.getValue();
|
|
|
|
}
|
|
|
|
|
2018-07-13 20:48:07 +03:00
|
|
|
const AbsolutePath& EdenConfig::getUserIgnoreFile() const {
|
|
|
|
return userIgnoreFile_.getValue();
|
2018-07-10 02:53:17 +03:00
|
|
|
}
|
|
|
|
|
2018-10-24 03:03:12 +03:00
|
|
|
const optional<AbsolutePath> EdenConfig::getClientCertificate() const {
|
2018-08-20 21:07:34 +03:00
|
|
|
auto value = clientCertificate_.getValue();
|
|
|
|
|
|
|
|
if (value == kUnspecifiedDefault) {
|
2018-10-24 03:03:12 +03:00
|
|
|
return std::nullopt;
|
2018-08-20 21:07:34 +03:00
|
|
|
}
|
|
|
|
return value;
|
2018-08-15 20:49:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool EdenConfig::getUseMononoke() const {
|
|
|
|
return useMononoke_.getValue();
|
|
|
|
}
|
|
|
|
|
2018-10-11 00:44:49 +03:00
|
|
|
const std::string& EdenConfig::getMononokeTierName() const {
|
|
|
|
return mononokeTierName_.getValue();
|
|
|
|
}
|
|
|
|
|
2018-10-31 21:48:12 +03:00
|
|
|
std::optional<std::string> EdenConfig::getMononokeHostName() const {
|
|
|
|
auto value = mononokeHostName_.getValue();
|
|
|
|
if (value.empty()) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t EdenConfig::getMononokePort() const {
|
|
|
|
return mononokePort_.getValue();
|
|
|
|
}
|
|
|
|
|
2019-03-12 00:20:44 +03:00
|
|
|
const std::string& EdenConfig::getMononokeConnectionType() const {
|
|
|
|
return mononokeConnectionType_.getValue();
|
|
|
|
}
|
|
|
|
|
2018-07-10 02:53:17 +03:00
|
|
|
void EdenConfig::setUserConfigPath(AbsolutePath userConfigPath) {
|
|
|
|
userConfigPath_ = userConfigPath;
|
|
|
|
}
|
|
|
|
void EdenConfig::setSystemConfigDir(AbsolutePath systemConfigDir) {
|
|
|
|
systemConfigDir_ = systemConfigDir;
|
|
|
|
}
|
|
|
|
void EdenConfig::setSystemConfigPath(AbsolutePath systemConfigPath) {
|
|
|
|
systemConfigPath_ = systemConfigPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EdenConfig::setSystemIgnoreFile(
|
|
|
|
AbsolutePath systemIgnoreFile,
|
|
|
|
ConfigSource configSource) {
|
|
|
|
return systemIgnoreFile_.setValue(systemIgnoreFile, configSource);
|
|
|
|
}
|
|
|
|
|
|
|
|
void EdenConfig::setEdenDir(AbsolutePath edenDir, ConfigSource configSource) {
|
|
|
|
return edenDir_.setValue(edenDir, configSource);
|
|
|
|
}
|
|
|
|
|
2018-07-13 20:48:07 +03:00
|
|
|
void EdenConfig::setUserIgnoreFile(
|
|
|
|
AbsolutePath userIgnoreFile,
|
2018-07-10 02:53:17 +03:00
|
|
|
ConfigSource configSource) {
|
2018-07-13 20:48:07 +03:00
|
|
|
return userIgnoreFile_.setValue(userIgnoreFile, configSource);
|
2018-07-10 02:53:17 +03:00
|
|
|
}
|
|
|
|
|
2018-08-15 20:49:37 +03:00
|
|
|
void EdenConfig::setClientCertificate(
|
|
|
|
AbsolutePath clientCertificate,
|
|
|
|
ConfigSource configSource) {
|
|
|
|
return clientCertificate_.setValue(clientCertificate, configSource);
|
|
|
|
}
|
|
|
|
|
|
|
|
void EdenConfig::setUseMononoke(bool useMononoke, ConfigSource configSource) {
|
|
|
|
return useMononoke_.setValue(useMononoke, configSource);
|
|
|
|
}
|
|
|
|
|
2018-07-10 02:53:17 +03:00
|
|
|
bool hasConfigFileChanged(
|
|
|
|
AbsolutePath configFileName,
|
|
|
|
const struct stat* oldStat) {
|
|
|
|
bool fileChangeDetected{false};
|
|
|
|
struct stat currentStat;
|
|
|
|
|
|
|
|
// We are using stat to check for file deltas. Since we don't open file,
|
|
|
|
// there is no chance of TOCTOU attack.
|
|
|
|
int rslt = stat(configFileName.c_str(), ¤tStat);
|
|
|
|
|
|
|
|
// Treat config file as if not present on error.
|
|
|
|
// Log error if not ENOENT as they are unexpected and useful for debugging.
|
|
|
|
if (rslt != 0) {
|
|
|
|
if (errno != ENOENT) {
|
|
|
|
XLOG(WARN) << "error accessing config file " << configFileName << ": "
|
|
|
|
<< folly::errnoStr(errno);
|
|
|
|
}
|
|
|
|
// We use all 0's to check if a file is created/deleted
|
|
|
|
memset(¤tStat, 0, sizeof(currentStat));
|
|
|
|
}
|
2018-11-22 03:36:53 +03:00
|
|
|
if (!equalStats(currentStat, *oldStat)) {
|
2018-07-10 02:53:17 +03:00
|
|
|
fileChangeDetected = true;
|
|
|
|
}
|
|
|
|
return fileChangeDetected;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool EdenConfig::hasUserConfigFileChanged() const {
|
|
|
|
return hasConfigFileChanged(getUserConfigPath(), &userConfigFileStat_);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool EdenConfig::hasSystemConfigFileChanged() const {
|
|
|
|
return hasConfigFileChanged(getSystemConfigPath(), &systemConfigFileStat_);
|
|
|
|
}
|
|
|
|
|
|
|
|
const AbsolutePath& EdenConfig::getUserConfigPath() const {
|
|
|
|
return userConfigPath_;
|
|
|
|
}
|
|
|
|
|
|
|
|
const AbsolutePath& EdenConfig::getSystemConfigPath() const {
|
|
|
|
return systemConfigPath_;
|
|
|
|
}
|
|
|
|
|
|
|
|
const AbsolutePath& EdenConfig::getSystemConfigDir() const {
|
|
|
|
return systemConfigDir_;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EdenConfig::clearAll(ConfigSource configSource) {
|
|
|
|
for (const auto& sectionEntry : configMap_) {
|
|
|
|
for (auto& keyEntry : sectionEntry.second) {
|
|
|
|
keyEntry.second->clearValue(configSource);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void getConfigStat(
|
|
|
|
AbsolutePathPiece configPath,
|
|
|
|
int configFd,
|
|
|
|
struct stat* configStat) {
|
|
|
|
int statRslt{-1};
|
|
|
|
if (configFd >= 0) {
|
|
|
|
statRslt = fstat(configFd, configStat);
|
|
|
|
// Report failure that is not due to ENOENT
|
|
|
|
if (statRslt != 0) {
|
|
|
|
XLOG(WARN) << "error accessing config file " << configPath << ": "
|
|
|
|
<< folly::errnoStr(errno);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We use all 0's to check if a file is created/deleted
|
|
|
|
if (statRslt != 0) {
|
|
|
|
memset(configStat, 0, sizeof(struct stat));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EdenConfig::loadSystemConfig() {
|
2019-06-05 21:47:01 +03:00
|
|
|
clearAll(ConfigSource::SystemConfig);
|
2018-07-10 02:53:17 +03:00
|
|
|
loadConfig(
|
2019-06-05 21:47:01 +03:00
|
|
|
systemConfigPath_, ConfigSource::SystemConfig, &systemConfigFileStat_);
|
2018-07-10 02:53:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void EdenConfig::loadUserConfig() {
|
2019-06-05 21:47:01 +03:00
|
|
|
clearAll(ConfigSource::UserConfig);
|
|
|
|
loadConfig(userConfigPath_, ConfigSource::UserConfig, &userConfigFileStat_);
|
2018-07-10 02:53:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void EdenConfig::loadConfig(
|
|
|
|
AbsolutePathPiece path,
|
|
|
|
ConfigSource configSource,
|
|
|
|
struct stat* configFileStat) {
|
|
|
|
struct stat configStat;
|
|
|
|
// Load the config path and update its stat information
|
|
|
|
auto configFd = open(path.copy().c_str(), O_RDONLY);
|
|
|
|
if (configFd < 0) {
|
|
|
|
if (errno != ENOENT) {
|
|
|
|
XLOG(WARN) << "error accessing config file " << path << ": "
|
|
|
|
<< folly::errnoStr(errno);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
getConfigStat(path, configFd, &configStat);
|
|
|
|
memcpy(configFileStat, &configStat, sizeof(struct stat));
|
|
|
|
if (configFd >= 0) {
|
|
|
|
parseAndApplyConfigFile(configFd, path, configSource);
|
|
|
|
}
|
|
|
|
SCOPE_EXIT {
|
2019-06-22 03:01:36 +03:00
|
|
|
if (configFd >= 0) {
|
|
|
|
close(configFd);
|
|
|
|
}
|
2018-07-10 02:53:17 +03:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-08-31 23:00:09 +03:00
|
|
|
namespace {
|
|
|
|
// This is a bit gross. We have enough type information in the toml
|
|
|
|
// file to know when an option is a boolean, but at the moment our
|
|
|
|
// intermediate layer stringly-types all the data. When the upper
|
|
|
|
// layers want to consume a bool, they expect to do so by consuming
|
|
|
|
// the string representation of it.
|
|
|
|
// This helper performs the reverse transformation so that we allow
|
|
|
|
// users to specify their configuration as a true boolean type.
|
|
|
|
cpptoml::option<std::string> itemAsString(
|
|
|
|
const std::shared_ptr<cpptoml::table>& currSection,
|
|
|
|
const std::string& entryKey) {
|
|
|
|
auto valueStr = currSection->get_as<std::string>(entryKey);
|
|
|
|
if (valueStr) {
|
|
|
|
return valueStr;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto valueBool = currSection->get_as<bool>(entryKey);
|
|
|
|
if (valueBool) {
|
|
|
|
return cpptoml::option<std::string>(*valueBool ? "true" : "false");
|
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2018-07-10 02:53:17 +03:00
|
|
|
void EdenConfig::parseAndApplyConfigFile(
|
|
|
|
int configFd,
|
|
|
|
AbsolutePathPiece configPath,
|
|
|
|
ConfigSource configSource) {
|
|
|
|
std::shared_ptr<cpptoml::table> configRoot;
|
|
|
|
std::map<std::string, std::string> attrMap;
|
|
|
|
attrMap["HOME"] = userHomePath_.value();
|
2018-08-06 23:49:33 +03:00
|
|
|
attrMap["USER"] = userName_;
|
2019-03-15 04:13:29 +03:00
|
|
|
attrMap["USER_ID"] = std::to_string(userID_);
|
2018-07-10 02:53:17 +03:00
|
|
|
|
|
|
|
try {
|
|
|
|
std::string fileContents;
|
|
|
|
if (!folly::readFile(configFd, fileContents)) {
|
|
|
|
XLOG(WARNING) << "Failed to read config file : " << configPath;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::istringstream is{fileContents};
|
|
|
|
cpptoml::parser p{is};
|
|
|
|
configRoot = p.parse();
|
|
|
|
} catch (const cpptoml::parse_exception& ex) {
|
|
|
|
XLOG(WARNING) << "Failed to parse config file : " << configPath
|
|
|
|
<< " skipping, error : " << ex.what();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Report unknown sections
|
|
|
|
for (const auto& sectionEntry : *configRoot) {
|
|
|
|
const auto& sectionName = sectionEntry.first;
|
|
|
|
auto configMapEntry = configMap_.find(sectionName);
|
|
|
|
if (configMapEntry == configMap_.end()) {
|
|
|
|
XLOG(WARNING) << "Ignoring unknown section in eden config: " << configPath
|
|
|
|
<< ", key: " << sectionName;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Load section
|
|
|
|
auto currSection = sectionEntry.second->as_table();
|
|
|
|
if (currSection) {
|
|
|
|
// Report unknown config settings.
|
|
|
|
for (const auto& entry : *currSection) {
|
|
|
|
const auto& entryKey = entry.first;
|
|
|
|
auto configMapKeyEntry = configMapEntry->second.find(entryKey);
|
|
|
|
if (configMapKeyEntry == configMapEntry->second.end()) {
|
|
|
|
XLOG(WARNING) << "Ignoring unknown key in eden config: " << configPath
|
|
|
|
<< ", " << sectionName << ":" << entryKey;
|
|
|
|
continue;
|
|
|
|
}
|
2018-08-31 23:00:09 +03:00
|
|
|
auto valueStr = itemAsString(currSection, entryKey);
|
2018-07-10 02:53:17 +03:00
|
|
|
if (valueStr) {
|
|
|
|
auto rslt = configMapKeyEntry->second->setStringValue(
|
|
|
|
*valueStr, attrMap, configSource);
|
|
|
|
if (rslt.hasError()) {
|
|
|
|
XLOG(WARNING) << "Ignoring invalid config entry " << configPath
|
|
|
|
<< " " << sectionName << ":" << entryKey
|
|
|
|
<< ", value '" << *valueStr << "' " << rslt.error();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
XLOG(WARNING) << "Ignoring invalid config entry " << configPath << " "
|
|
|
|
<< sectionName << ":" << entryKey
|
2018-08-31 23:00:09 +03:00
|
|
|
<< ", is not a string or boolean";
|
2018-07-10 02:53:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace eden
|
|
|
|
} // namespace facebook
|