mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-10 21:58:03 +00:00
Compare commits
5 Commits
tapanito/v
...
bthomee/co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6fe6c3cfa | ||
|
|
4910cae183 | ||
|
|
3fb37ef6be | ||
|
|
12ff0fb924 | ||
|
|
eaaee26a3a |
2
.github/scripts/rename/config.sh
vendored
2
.github/scripts/rename/config.sh
vendored
@@ -62,7 +62,7 @@ ${SED_COMMAND} -i 's@ripple/@xrpld/@g' src/test/core/Config_test.cpp
|
||||
${SED_COMMAND} -i 's/Rippled/File/g' src/test/core/Config_test.cpp
|
||||
|
||||
# Restore the old config file name in the code that maintains support for now.
|
||||
${SED_COMMAND} -i 's/kConfigLegacyName = "xrpld.cfg"/kConfigLegacyName = "rippled.cfg"/g' src/xrpld/core/detail/Config.cpp
|
||||
${SED_COMMAND} -i 's/kConfigLegacyName = "xrpld.cfg"/kConfigLegacyName = "rippled.cfg"/g' src/xrpld/core/Config.h
|
||||
|
||||
# Restore an URL.
|
||||
${SED_COMMAND} -i 's/connect-your-xrpld-to-the-xrp-test-net.html/connect-your-rippled-to-the-xrp-test-net.html/g' cfg/xrpld-example.cfg
|
||||
|
||||
2
.github/scripts/rename/docs.sh
vendored
2
.github/scripts/rename/docs.sh
vendored
@@ -90,7 +90,7 @@ ${SED_COMMAND} -i 's/www.ripple.com/www.xrpl.org/g' src/test/protocol/Seed_test.
|
||||
# Restore specific changes.
|
||||
${SED_COMMAND} -i 's@b5efcc/src/xrpld@b5efcc/src/ripple@' include/xrpl/protocol/README.md
|
||||
${SED_COMMAND} -i 's/dbPrefix_ = "xrpldb"/dbPrefix_ = "rippledb"/' src/xrpld/app/misc/SHAMapStoreImp.h # cspell: disable-line
|
||||
${SED_COMMAND} -i 's/kConfigLegacyName = "xrpld.cfg"/kConfigLegacyName = "rippled.cfg"/' src/xrpld/core/detail/Config.cpp
|
||||
${SED_COMMAND} -i 's/kConfigLegacyName = "xrpld.cfg"/kConfigLegacyName = "rippled.cfg"/' src/xrpld/core/Config.h
|
||||
|
||||
popd
|
||||
echo "Renaming complete."
|
||||
|
||||
@@ -20,10 +20,13 @@
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <streambuf>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <typeinfo>
|
||||
@@ -122,6 +125,149 @@ backend=sqlite
|
||||
return std::format(kConfigContentsTemplate, dbPathSection, valFileSection);
|
||||
}
|
||||
|
||||
// setenv and unsetenv are POSIX, and Windows declares neither, so this guard is
|
||||
// compiled only where the test cases that use it are.
|
||||
#if BOOST_OS_LINUX || BOOST_OS_MACOS
|
||||
|
||||
/**
|
||||
* Set an environment variable and restore its previous value when done.
|
||||
*
|
||||
* The previous value is copied on construction rather than kept as the pointer
|
||||
* `getenv` returned, because POSIX allows a later `setenv` to release that
|
||||
* string. Restoring through the pointer can therefore read freed memory and
|
||||
* restore a wrong value. Restoring in the destructor also means an assertion
|
||||
* that throws no longer leaves the variable overwritten.
|
||||
*/
|
||||
class EnvVarGuard
|
||||
{
|
||||
private:
|
||||
std::string const name_;
|
||||
std::optional<std::string> const saved_;
|
||||
|
||||
/**
|
||||
* Read an environment variable into a value this guard owns.
|
||||
*
|
||||
* @param name Name of the environment variable to read.
|
||||
* @return The variable's value, or std::nullopt when it is not set.
|
||||
*/
|
||||
[[nodiscard]] static std::optional<std::string>
|
||||
read(char const* name)
|
||||
{
|
||||
if (char const* const value = std::getenv(name); value != nullptr)
|
||||
return std::string{value};
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Give an environment variable a value, or remove it.
|
||||
*
|
||||
* @param name Name of the environment variable to write.
|
||||
* @param value Value to set, or std::nullopt to remove the variable.
|
||||
*/
|
||||
static void
|
||||
apply(char const* name, std::optional<std::string> const& value) noexcept
|
||||
{
|
||||
// unsetenv succeeds on a variable that is not set, so the std::nullopt
|
||||
// case needs no separate check.
|
||||
if (value)
|
||||
{
|
||||
setenv(name, value->c_str(), 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsetenv(name);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Save the variable's current value, then write the given one.
|
||||
*
|
||||
* @param name Name of the environment variable to set.
|
||||
* @param value Value to set, or std::nullopt to remove the variable.
|
||||
*/
|
||||
EnvVarGuard(char const* name, std::optional<std::string> const& value)
|
||||
: name_(name), saved_(read(name))
|
||||
{
|
||||
apply(name_.c_str(), value);
|
||||
}
|
||||
|
||||
EnvVarGuard(EnvVarGuard const&) = delete;
|
||||
EnvVarGuard&
|
||||
operator=(EnvVarGuard const&) = delete;
|
||||
|
||||
/**
|
||||
* Restore the value the variable held before construction.
|
||||
*/
|
||||
~EnvVarGuard()
|
||||
{
|
||||
// Nothing here can throw: setenv and unsetenv are C functions, and
|
||||
// neither std::string::c_str nor std::optional::operator-> throws. The
|
||||
// noexcept on apply keeps that true, so unlike ~FileCfgGuard below
|
||||
// this destructor needs no try/catch.
|
||||
apply(name_.c_str(), saved_);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // BOOST_OS_LINUX || BOOST_OS_MACOS
|
||||
|
||||
/**
|
||||
* Send a stream's output to a buffer of our own, and restore it when done.
|
||||
*
|
||||
* Restoring in the destructor means an assertion that throws cannot leave the
|
||||
* redirect in place. A leaked redirect on std::cerr would swallow the output of
|
||||
* every later test case in the run.
|
||||
*/
|
||||
class StreamRedirectGuard
|
||||
{
|
||||
private:
|
||||
// Declaration order matters here. The constructor initializes saved_ from
|
||||
// captured_.rdbuf(), and members initialize in declaration order, so
|
||||
// captured_ must stay declared before saved_. Moving saved_ above it would
|
||||
// call rdbuf() on an unconstructed stream, which no warning reports because
|
||||
// the member initializer list is already in declaration order.
|
||||
std::ostream& stream_;
|
||||
std::ostringstream captured_;
|
||||
std::streambuf* const saved_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Send the stream's output to this guard's own buffer.
|
||||
*
|
||||
* @param stream Stream to redirect for the guard's lifetime.
|
||||
*/
|
||||
explicit StreamRedirectGuard(std::ostream& stream)
|
||||
: stream_(stream), saved_(stream.rdbuf(captured_.rdbuf()))
|
||||
{
|
||||
}
|
||||
|
||||
StreamRedirectGuard(StreamRedirectGuard const&) = delete;
|
||||
StreamRedirectGuard&
|
||||
operator=(StreamRedirectGuard const&) = delete;
|
||||
|
||||
/**
|
||||
* Read what the stream has written so far.
|
||||
*
|
||||
* @return Everything written to the stream since construction.
|
||||
*/
|
||||
[[nodiscard]] std::string
|
||||
str() const
|
||||
{
|
||||
return captured_.str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the stream's output back where it went before.
|
||||
*/
|
||||
~StreamRedirectGuard()
|
||||
{
|
||||
// std::ios::rdbuf(std::streambuf*) does not throw, so this destructor
|
||||
// needs no try/catch.
|
||||
stream_.rdbuf(saved_);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Write an xrpld config file and remove when done.
|
||||
*/
|
||||
@@ -156,11 +302,13 @@ public:
|
||||
dataDir_ = subdir() / path(Config::kDatabaseDirName);
|
||||
|
||||
rmDataDir_ = !exists(dataDir_);
|
||||
config_.setup(
|
||||
file_.string(),
|
||||
/* bQuiet */ true,
|
||||
/* bSilent */ false,
|
||||
/* bStandalone */ false);
|
||||
test_.expect(
|
||||
config_.setup(
|
||||
file_.string(),
|
||||
/* bQuiet */ true,
|
||||
/* bSilent */ false,
|
||||
/* bStandalone */ false),
|
||||
"setup failed for " + file_.string());
|
||||
}
|
||||
|
||||
[[nodiscard]] Config const&
|
||||
@@ -327,13 +475,21 @@ port_wss_admin
|
||||
|
||||
// Load the config file from the current directory and verify it.
|
||||
Config c;
|
||||
c.setup("", true, false, true);
|
||||
BEAST_EXPECT(c.setup("", true, false, true));
|
||||
BEAST_EXPECT(c.section(Sections::kDebugLogfile).values().size() == 1);
|
||||
BEAST_EXPECT(
|
||||
c.section(Sections::kDebugLogfile).values()[0] ==
|
||||
"/Users/dummy/xrpld/config/log/debug.log");
|
||||
}
|
||||
|
||||
// A named config file that cannot be read. Naming one is an explicit
|
||||
// request for that file, so setup() must not fall back to defaults.
|
||||
{
|
||||
TempDir const td;
|
||||
Config c;
|
||||
BEAST_EXPECT(!c.setup(td.file("does_not_exist.cfg"), true, false, true));
|
||||
}
|
||||
|
||||
// Config file in HOME or XDG_CONFIG_HOME directory.
|
||||
#if BOOST_OS_LINUX || BOOST_OS_MACOS
|
||||
for (auto const& configFile : configFiles)
|
||||
@@ -350,11 +506,10 @@ port_wss_admin
|
||||
|
||||
// Set the HOME and XDG_CONFIG_HOME environment variables. The
|
||||
// HOME variable is not used when XDG_CONFIG_HOME is set, but
|
||||
// must be set.
|
||||
char const* h = getenv("HOME");
|
||||
setenv("HOME", tc.path().c_str(), 1);
|
||||
char const* x = getenv("XDG_CONFIG_HOME");
|
||||
setenv("XDG_CONFIG_HOME", tc.path().c_str(), 1);
|
||||
// must be set. Both guards are declared after tc, so both
|
||||
// restore before tc removes the directory they point into.
|
||||
detail::EnvVarGuard const homeGuard("HOME", tc.path());
|
||||
detail::EnvVarGuard const xdgGuard("XDG_CONFIG_HOME", tc.path());
|
||||
|
||||
// Create the config file in '${XDG_CONFIG_HOME}/[systemName]'.
|
||||
path p = tc.file(systemName());
|
||||
@@ -366,15 +521,11 @@ port_wss_admin
|
||||
|
||||
// Load the config file from the config directory and verify it.
|
||||
Config c;
|
||||
c.setup("", true, false, true);
|
||||
BEAST_EXPECT(c.setup("", true, false, true));
|
||||
BEAST_EXPECT(c.section(Sections::kDebugLogfile).values().size() == 1);
|
||||
BEAST_EXPECT(
|
||||
c.section(Sections::kDebugLogfile).values()[0] ==
|
||||
"/Users/dummy/xrpld/config/log/debug.log");
|
||||
|
||||
// Restore the environment variables.
|
||||
(h != nullptr) ? setenv("HOME", h, 1) : unsetenv("HOME");
|
||||
(x != nullptr) ? setenv("XDG_CONFIG_HOME", x, 1) : unsetenv("XDG_CONFIG_HOME");
|
||||
}
|
||||
|
||||
// The XDG config directory is not set: the config file must be in a
|
||||
@@ -382,11 +533,11 @@ port_wss_admin
|
||||
{
|
||||
TempDir const tc;
|
||||
|
||||
// Set only the HOME environment variable.
|
||||
char const* h = getenv("HOME");
|
||||
setenv("HOME", tc.path().c_str(), 1);
|
||||
char const* x = getenv("XDG_CONFIG_HOME");
|
||||
unsetenv("XDG_CONFIG_HOME");
|
||||
// Set only the HOME environment variable. Both guards are
|
||||
// declared after tc, so both restore before tc removes the
|
||||
// directory HOME points into.
|
||||
detail::EnvVarGuard const homeGuard("HOME", tc.path());
|
||||
detail::EnvVarGuard const xdgGuard("XDG_CONFIG_HOME", std::nullopt);
|
||||
|
||||
// Create the config file in '${HOME}/.config/[systemName]'.
|
||||
std::string s = ".config";
|
||||
@@ -402,16 +553,66 @@ port_wss_admin
|
||||
|
||||
// Load the config file from the config directory and verify it.
|
||||
Config c;
|
||||
c.setup("", true, false, true);
|
||||
BEAST_EXPECT(c.setup("", true, false, true));
|
||||
BEAST_EXPECT(c.section(Sections::kDebugLogfile).values().size() == 1);
|
||||
BEAST_EXPECT(
|
||||
c.section(Sections::kDebugLogfile).values()[0] ==
|
||||
"/Users/dummy/xrpld/config/log/debug.log");
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the environment variables.
|
||||
(h != nullptr) ? setenv("HOME", h, 1) : unsetenv("HOME");
|
||||
if (x != nullptr)
|
||||
setenv("XDG_CONFIG_HOME", x, 1);
|
||||
// No config file under either name in any searched directory.
|
||||
{
|
||||
// The system config directory is the last one searched and is not
|
||||
// under the test's control, so only run this when it holds no
|
||||
// config file.
|
||||
path const systemDir = path("/etc") / systemName();
|
||||
if (exists(systemDir / std::string{Config::kConfigFileName}) ||
|
||||
exists(systemDir / std::string{Config::kConfigLegacyName}))
|
||||
{
|
||||
log << "Skipping: " << systemDir.string() << " holds a config file." << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Point the working directory and HOME at empty temporary
|
||||
// directories, so neither holds a config file. The guards are
|
||||
// declared after tc, so they restore before it is removed.
|
||||
TempDir const td;
|
||||
current_path(td.path());
|
||||
TempDir const tc;
|
||||
|
||||
detail::EnvVarGuard const homeGuard("HOME", tc.path());
|
||||
detail::EnvVarGuard const xdgGuard("XDG_CONFIG_HOME", std::nullopt);
|
||||
|
||||
// The three directories setup() is expected to search and
|
||||
// report. Read the working directory back rather than reusing
|
||||
// td.path(), because current_path() can resolve a symlink and
|
||||
// return a different spelling of the same directory.
|
||||
path const searchedCwd = current_path();
|
||||
std::string const searchedXdgDir = tc.path() + "/.config/" + systemName();
|
||||
std::string const searchedSystemDir = systemDir.string();
|
||||
|
||||
// setup() must report the failure rather than fall back to the
|
||||
// last directory it searched. Capture and restore stderr around
|
||||
// the call only, so that an assertion below cannot have its own
|
||||
// output swallowed by the capture.
|
||||
Config c;
|
||||
bool setupResult = true;
|
||||
std::string reported;
|
||||
{
|
||||
detail::StreamRedirectGuard const capture(std::cerr);
|
||||
setupResult = c.setup("", true, false, true);
|
||||
reported = capture.str();
|
||||
}
|
||||
|
||||
BEAST_EXPECT(!setupResult);
|
||||
|
||||
// Assert on the directory paths rather than on the wording of
|
||||
// the header, so that a later change to that sentence does not
|
||||
// break this.
|
||||
BEAST_EXPECT(reported.contains(searchedCwd.string()));
|
||||
BEAST_EXPECT(reported.contains(searchedXdgDir));
|
||||
BEAST_EXPECT(reported.contains(searchedSystemDir));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1008,11 +1209,11 @@ trust-these-validators.gov
|
||||
*/
|
||||
{
|
||||
Config config;
|
||||
config.setup(
|
||||
BEAST_EXPECT(config.setup(
|
||||
cfg.configFile(),
|
||||
/*bQuiet*/ false,
|
||||
/* bSilent */ false,
|
||||
/* bStandalone */ false);
|
||||
/* bStandalone */ false));
|
||||
BEAST_EXPECT(!config.quiet());
|
||||
BEAST_EXPECT(!config.silent());
|
||||
BEAST_EXPECT(!config.standalone());
|
||||
@@ -1021,11 +1222,11 @@ trust-these-validators.gov
|
||||
}
|
||||
{
|
||||
Config config;
|
||||
config.setup(
|
||||
BEAST_EXPECT(config.setup(
|
||||
cfg.configFile(),
|
||||
/*bQuiet*/ true,
|
||||
/* bSilent */ false,
|
||||
/* bStandalone */ false);
|
||||
/* bStandalone */ false));
|
||||
BEAST_EXPECT(config.quiet());
|
||||
BEAST_EXPECT(!config.silent());
|
||||
BEAST_EXPECT(!config.standalone());
|
||||
@@ -1034,11 +1235,11 @@ trust-these-validators.gov
|
||||
}
|
||||
{
|
||||
Config config;
|
||||
config.setup(
|
||||
BEAST_EXPECT(config.setup(
|
||||
cfg.configFile(),
|
||||
/*bQuiet*/ false,
|
||||
/* bSilent */ true,
|
||||
/* bStandalone */ false);
|
||||
/* bStandalone */ false));
|
||||
BEAST_EXPECT(config.quiet());
|
||||
BEAST_EXPECT(config.silent());
|
||||
BEAST_EXPECT(!config.standalone());
|
||||
@@ -1047,11 +1248,11 @@ trust-these-validators.gov
|
||||
}
|
||||
{
|
||||
Config config;
|
||||
config.setup(
|
||||
BEAST_EXPECT(config.setup(
|
||||
cfg.configFile(),
|
||||
/*bQuiet*/ true,
|
||||
/* bSilent */ true,
|
||||
/* bStandalone */ false);
|
||||
/* bStandalone */ false));
|
||||
BEAST_EXPECT(config.quiet());
|
||||
BEAST_EXPECT(config.silent());
|
||||
BEAST_EXPECT(!config.standalone());
|
||||
@@ -1060,11 +1261,11 @@ trust-these-validators.gov
|
||||
}
|
||||
{
|
||||
Config config;
|
||||
config.setup(
|
||||
BEAST_EXPECT(config.setup(
|
||||
cfg.configFile(),
|
||||
/*bQuiet*/ false,
|
||||
/* bSilent */ false,
|
||||
/* bStandalone */ true);
|
||||
/* bStandalone */ true));
|
||||
BEAST_EXPECT(!config.quiet());
|
||||
BEAST_EXPECT(!config.silent());
|
||||
BEAST_EXPECT(config.standalone());
|
||||
@@ -1073,11 +1274,11 @@ trust-these-validators.gov
|
||||
}
|
||||
{
|
||||
Config config;
|
||||
config.setup(
|
||||
BEAST_EXPECT(config.setup(
|
||||
cfg.configFile(),
|
||||
/*bQuiet*/ true,
|
||||
/* bSilent */ false,
|
||||
/* bStandalone */ true);
|
||||
/* bStandalone */ true));
|
||||
BEAST_EXPECT(config.quiet());
|
||||
BEAST_EXPECT(!config.silent());
|
||||
BEAST_EXPECT(config.standalone());
|
||||
@@ -1086,11 +1287,11 @@ trust-these-validators.gov
|
||||
}
|
||||
{
|
||||
Config config;
|
||||
config.setup(
|
||||
BEAST_EXPECT(config.setup(
|
||||
cfg.configFile(),
|
||||
/*bQuiet*/ false,
|
||||
/* bSilent */ true,
|
||||
/* bStandalone */ true);
|
||||
/* bStandalone */ true));
|
||||
BEAST_EXPECT(config.quiet());
|
||||
BEAST_EXPECT(config.silent());
|
||||
BEAST_EXPECT(config.standalone());
|
||||
@@ -1099,11 +1300,11 @@ trust-these-validators.gov
|
||||
}
|
||||
{
|
||||
Config config;
|
||||
config.setup(
|
||||
BEAST_EXPECT(config.setup(
|
||||
cfg.configFile(),
|
||||
/*bQuiet*/ true,
|
||||
/* bSilent */ true,
|
||||
/* bStandalone */ true);
|
||||
/* bStandalone */ true));
|
||||
BEAST_EXPECT(config.quiet());
|
||||
BEAST_EXPECT(config.silent());
|
||||
BEAST_EXPECT(config.standalone());
|
||||
|
||||
@@ -574,9 +574,23 @@ run(int argc, char** argv)
|
||||
|
||||
auto configFile = vm.contains("conf") ? vm["conf"].as<std::string>() : std::string();
|
||||
|
||||
// config file, quiet flag.
|
||||
config->setup(
|
||||
configFile, vm.contains("quiet"), vm.contains("silent"), vm.contains("standalone"));
|
||||
// config file, quiet flag. Setup reports a config file it cannot find or
|
||||
// read by returning false, and invalid config contents by throwing, so both
|
||||
// are handled here rather than left to escape.
|
||||
try
|
||||
{
|
||||
if (!config->setup(
|
||||
configFile, vm.contains("quiet"), vm.contains("silent"), vm.contains("standalone")))
|
||||
{
|
||||
// setup() has already reported the reason it failed.
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
std::cerr << "xrpld: invalid configuration: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (vm.contains("vacuum"))
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -88,10 +89,10 @@ class Config : public BasicConfig
|
||||
{
|
||||
public:
|
||||
// Settings related to the configuration file location and directories
|
||||
static char const* const kConfigFileName;
|
||||
static char const* const kConfigLegacyName;
|
||||
static char const* const kDatabaseDirName;
|
||||
static char const* const kValidatorsFileName;
|
||||
static constexpr std::string_view kConfigFileName = "xrpld.cfg";
|
||||
static constexpr std::string_view kConfigLegacyName = "rippled.cfg";
|
||||
static constexpr std::string_view kDatabaseDirName = "db";
|
||||
static constexpr std::string_view kValidatorsFileName = "validators.txt";
|
||||
|
||||
/**
|
||||
* Returns the full path and filename of the debug log file.
|
||||
@@ -99,17 +100,22 @@ public:
|
||||
[[nodiscard]] std::filesystem::path
|
||||
getDebugLogFile() const;
|
||||
|
||||
private:
|
||||
std::filesystem::path configFile_;
|
||||
|
||||
public:
|
||||
std::filesystem::path configDir;
|
||||
|
||||
private:
|
||||
std::filesystem::path debugLogfile_;
|
||||
|
||||
void
|
||||
load();
|
||||
/**
|
||||
* Reads and parses the given config file.
|
||||
*
|
||||
* @param configFile Path of the config file to read.
|
||||
* @return True when the file was read, false when it could not be, in
|
||||
* which case the reason has been written to stderr and the
|
||||
* configuration is left unchanged.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
load(std::filesystem::path const& configFile);
|
||||
beast::Journal const j_;
|
||||
|
||||
bool quiet_ = false; // Minimize logging verbosity.
|
||||
@@ -329,9 +335,34 @@ public:
|
||||
public:
|
||||
Config();
|
||||
|
||||
/* Be very careful to make sure these bool params
|
||||
are in the right order. */
|
||||
void
|
||||
/**
|
||||
* Sets up the configuration, and determines the config and data
|
||||
* directories.
|
||||
*
|
||||
* When no config file is named, searches the current working directory,
|
||||
* the XDG config directory, and the system config directory, in that
|
||||
* order, and takes the first one that holds a config file under either the
|
||||
* current or the legacy name. When none of them does, writes the searched
|
||||
* directories to stderr and fails.
|
||||
*
|
||||
* Failing to read the config file, whether it was named or found, is also
|
||||
* a failure, and writes the reason to stderr.
|
||||
*
|
||||
* Be very careful to make sure these bool params are in the right order.
|
||||
*
|
||||
* @param strConf Path of the config file, or empty to search for it.
|
||||
* @param bQuiet Whether to minimize logging verbosity.
|
||||
* @param bSilent Whether to write no console output after startup.
|
||||
* @param bStandalone Whether to operate in stand-alone mode.
|
||||
* @return True when a config file was named or found, and read. False when
|
||||
* no config file could be found in any searched directory, or when
|
||||
* the file that was named or found could not be read. Both cases
|
||||
* have written their reason to stderr first.
|
||||
* @throws std::runtime_error When the config file contents are invalid, or
|
||||
* when the data directory cannot be created. Invalid contents are
|
||||
* reported this way rather than through the return value.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
setup(std::string const& strConf, bool bQuiet, bool bSilent, bool bStandalone);
|
||||
|
||||
void
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <system_error>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
@@ -246,11 +247,6 @@ getSingleSection(
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
char const* const Config::kConfigFileName = "xrpld.cfg";
|
||||
char const* const Config::kConfigLegacyName = "rippled.cfg";
|
||||
char const* const Config::kDatabaseDirName = "db";
|
||||
char const* const Config::kValidatorsFileName = "validators.txt";
|
||||
|
||||
[[nodiscard]] static std::string
|
||||
getEnvVar(char const* name)
|
||||
{
|
||||
@@ -302,7 +298,7 @@ Config::setupControl(bool bQuiet, bool bSilent, bool bStandalone)
|
||||
XRPL_ASSERT(nodeSize <= 4, "xrpl::Config::setupControl : node size is set");
|
||||
}
|
||||
|
||||
void
|
||||
bool
|
||||
Config::setup(std::string const& strConf, bool bQuiet, bool bSilent, bool bStandalone)
|
||||
{
|
||||
setupControl(bQuiet, bSilent, bStandalone);
|
||||
@@ -314,73 +310,99 @@ Config::setup(std::string const& strConf, bool bQuiet, bool bSilent, bool bStand
|
||||
// directory.
|
||||
std::filesystem::path dataDir;
|
||||
|
||||
// The config file to read. It stays unset when the search for one finds
|
||||
// nothing, which is the only way this function fails.
|
||||
std::optional<std::filesystem::path> configFile;
|
||||
|
||||
if (!strConf.empty())
|
||||
{
|
||||
// --conf=<path> : everything is relative that file.
|
||||
configFile_ = strConf;
|
||||
configDir = std::filesystem::absolute(configFile_);
|
||||
configFile = strConf;
|
||||
configDir = std::filesystem::absolute(*configFile);
|
||||
configDir.remove_filename();
|
||||
dataDir = configDir / kDatabaseDirName;
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
// A config directory to search, paired with the data directory that
|
||||
// goes with it.
|
||||
struct Candidate
|
||||
{
|
||||
// Check if either of the config files exist in the current working
|
||||
// directory, in which case the databases will be stored in a
|
||||
// subdirectory.
|
||||
configDir = std::filesystem::current_path();
|
||||
dataDir = configDir / kDatabaseDirName;
|
||||
configFile_ = configDir / kConfigFileName;
|
||||
if (std::filesystem::exists(configFile_))
|
||||
break;
|
||||
configFile_ = configDir / kConfigLegacyName;
|
||||
if (std::filesystem::exists(configFile_))
|
||||
break;
|
||||
std::filesystem::path configDir;
|
||||
std::filesystem::path dataDir;
|
||||
};
|
||||
|
||||
// Check if the home directory is set, and optionally the XDG config
|
||||
// and/or data directories, as the config may be there. See
|
||||
// http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html.
|
||||
auto const strHome = getEnvVar("HOME");
|
||||
if (!strHome.empty())
|
||||
// The candidates to search, in order. There are at most three.
|
||||
std::vector<Candidate> candidates;
|
||||
candidates.reserve(3);
|
||||
|
||||
// The current working directory, with the databases in a subdirectory.
|
||||
auto const cwd = std::filesystem::current_path();
|
||||
candidates.emplace_back(cwd, cwd / kDatabaseDirName);
|
||||
|
||||
// The XDG config and data directories, which only apply when the home
|
||||
// directory is set. See
|
||||
// http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html.
|
||||
if (auto const strHome = getEnvVar("HOME"); !strHome.empty())
|
||||
{
|
||||
auto strXdgConfigHome = getEnvVar("XDG_CONFIG_HOME");
|
||||
if (strXdgConfigHome.empty())
|
||||
{
|
||||
auto strXdgConfigHome = getEnvVar("XDG_CONFIG_HOME");
|
||||
auto strXdgDataHome = getEnvVar("XDG_DATA_HOME");
|
||||
if (strXdgConfigHome.empty())
|
||||
{
|
||||
// $XDG_CONFIG_HOME was not set, use default based on $HOME.
|
||||
strXdgConfigHome = strHome + "/.config";
|
||||
}
|
||||
if (strXdgDataHome.empty())
|
||||
{
|
||||
// $XDG_DATA_HOME was not set, use default based on $HOME.
|
||||
strXdgDataHome = strHome + "/.local/share";
|
||||
}
|
||||
// $XDG_CONFIG_HOME was not set, use default based on $HOME.
|
||||
strXdgConfigHome = strHome + "/.config";
|
||||
}
|
||||
auto strXdgDataHome = getEnvVar("XDG_DATA_HOME");
|
||||
if (strXdgDataHome.empty())
|
||||
{
|
||||
// $XDG_DATA_HOME was not set, use default based on $HOME.
|
||||
strXdgDataHome = strHome + "/.local/share";
|
||||
}
|
||||
candidates.emplace_back(
|
||||
strXdgConfigHome + "/" + systemName(), strXdgDataHome + "/" + systemName());
|
||||
}
|
||||
|
||||
// Check if either of the config files exist in the XDG config
|
||||
// dir.
|
||||
dataDir = strXdgDataHome + "/" + systemName();
|
||||
configDir = strXdgConfigHome + "/" + systemName();
|
||||
configFile_ = configDir / kConfigFileName;
|
||||
if (std::filesystem::exists(configFile_))
|
||||
break;
|
||||
configFile_ = configDir / kConfigLegacyName;
|
||||
if (std::filesystem::exists(configFile_))
|
||||
// The system config directory, as a last resort.
|
||||
candidates.emplace_back("/etc/" + systemName(), "/var/lib/" + systemName());
|
||||
|
||||
// Take the first candidate directory holding a config file under either
|
||||
// name. When none of them does, configFile stays unset, while configDir
|
||||
// and dataDir keep the values of the last candidate.
|
||||
for (auto const& candidate : candidates)
|
||||
{
|
||||
configDir = candidate.configDir;
|
||||
dataDir = candidate.dataDir;
|
||||
|
||||
for (std::string_view const fileName : {kConfigFileName, kConfigLegacyName})
|
||||
{
|
||||
auto candidateFile = configDir / fileName;
|
||||
if (std::filesystem::exists(candidateFile))
|
||||
{
|
||||
configFile = std::move(candidateFile);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// As a last resort, check the system config directory.
|
||||
dataDir = "/var/lib/" + systemName();
|
||||
configDir = "/etc/" + systemName();
|
||||
configFile_ = configDir / kConfigFileName;
|
||||
if (std::filesystem::exists(configFile_))
|
||||
if (configFile)
|
||||
break;
|
||||
configFile_ = configDir / kConfigLegacyName;
|
||||
} while (false);
|
||||
}
|
||||
|
||||
if (!configFile)
|
||||
{
|
||||
// Report every directory searched. Naming only the last one would
|
||||
// suggest it is the only place a config file is expected. Report
|
||||
// this even when quiet, because it is the reason for failing.
|
||||
std::cerr << std::format(
|
||||
"No {} or {} found. Searched:\n", kConfigFileName, kConfigLegacyName);
|
||||
for (auto const& candidate : candidates)
|
||||
std::cerr << std::format(" {}\n", candidate.configDir.string());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Update default values
|
||||
load();
|
||||
if (!load(*configFile))
|
||||
return false;
|
||||
{
|
||||
// load() may have set a new value for the dataDir
|
||||
std::string const dbPath(legacy(Sections::kDatabasePath));
|
||||
@@ -415,6 +437,8 @@ Config::setup(std::string const& strConf, bool bQuiet, bool bSilent, bool bStand
|
||||
|
||||
Section const& nodeDbSection{section(Sections::kNodeDatabase)};
|
||||
getIfExists(nodeDbSection, Keys::kFastLoad, fastLoad);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 0 ports are allowed for unit tests, but still not allowed to be present in
|
||||
@@ -445,27 +469,34 @@ checkZeroPorts(Config const& config)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Config::load()
|
||||
bool
|
||||
Config::load(std::filesystem::path const& configFile)
|
||||
{
|
||||
// Report the path as a plain string. There is no std::formatter for
|
||||
// std::filesystem::path, and streaming one adds quotes of its own, which
|
||||
// would double up with the quotes written here.
|
||||
auto const fileName = configFile.string();
|
||||
|
||||
// NOTE: this writes to cerr because we want cout to be reserved
|
||||
// for the writing of the json response (so that stdout can be part of a
|
||||
// pipeline, for instance)
|
||||
if (!quiet_)
|
||||
std::cerr << "Loading: " << configFile_ << "\n";
|
||||
std::cerr << std::format("Loading: '{}'\n", fileName);
|
||||
|
||||
std::error_code ec;
|
||||
auto const fileContents = getFileContents(ec, configFile_);
|
||||
auto const fileContents = getFileContents(ec, configFile);
|
||||
|
||||
if (ec)
|
||||
{
|
||||
std::cerr << "Failed to read '" << configFile_ << "'." << ec.value() << ": " << ec.message()
|
||||
<< std::endl;
|
||||
return;
|
||||
std::cerr << std::format(
|
||||
"Failed to read '{}': {} (error {})\n", fileName, ec.message(), ec.value());
|
||||
return false;
|
||||
}
|
||||
|
||||
loadFromString(fileContents);
|
||||
checkZeroPorts(*this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
Reference in New Issue
Block a user