mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-11 22:28:02 +00:00
Compare commits
2 Commits
nb-baseuin
...
bthomee/co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8fabef3b50 | ||
|
|
f95f7889f9 |
@@ -194,6 +194,7 @@ tests.libxrpl > xrpl.resource
|
||||
tests.libxrpl > xrpl.server
|
||||
tests.libxrpl > xrpl.shamap
|
||||
tests.libxrpl > xrpl.tx
|
||||
tests.xrpld > xrpld.app
|
||||
tests.xrpld > xrpld.rpc
|
||||
tests.xrpld > xrpl.json
|
||||
tests.xrpld > xrpl.protocol
|
||||
|
||||
@@ -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>
|
||||
@@ -199,6 +202,53 @@ public:
|
||||
|
||||
#endif // _MSC_VER
|
||||
|
||||
/**
|
||||
* Send a stream's output to a buffer of our own, and restore it when done.
|
||||
*/
|
||||
class StreamRedirectGuard
|
||||
{
|
||||
private:
|
||||
// The saved_ member is initialized from captured_.rdbuf(), so captured_
|
||||
// must stay declared before it.
|
||||
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()
|
||||
{
|
||||
stream_.rdbuf(saved_);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Write an xrpld config file and remove when done.
|
||||
*/
|
||||
@@ -233,11 +283,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&
|
||||
@@ -404,13 +456,35 @@ 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 config file that cannot be read throws, whether it was named or
|
||||
// found by the search. The throw leaves the data directory alone, so
|
||||
// nothing is created beside the file that could not be read.
|
||||
{
|
||||
TempDir const td;
|
||||
Path const missing = Path(td.file("does_not_exist.cfg"));
|
||||
std::string error;
|
||||
try
|
||||
{
|
||||
Config c;
|
||||
BEAST_EXPECT(!c.setup(missing.string(), true, false, false));
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
error = e.what();
|
||||
}
|
||||
BEAST_EXPECT(error.starts_with("Failed to read '" + missing.string() + "'"));
|
||||
|
||||
// Nothing was read, so no data directory is created.
|
||||
BEAST_EXPECT(!exists(Path(td.path()) / std::string{Config::kDatabaseDirName}));
|
||||
}
|
||||
|
||||
// Config file in HOME or XDG_CONFIG_HOME directory.
|
||||
#ifndef _MSC_VER
|
||||
// Save the values the guards below must put back.
|
||||
@@ -445,7 +519,7 @@ 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] ==
|
||||
@@ -480,7 +554,7 @@ 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] ==
|
||||
@@ -491,6 +565,60 @@ port_wss_admin
|
||||
BEAST_EXPECT(detail::envVar("HOME") == home);
|
||||
BEAST_EXPECT(detail::envVar("XDG_CONFIG_HOME") == xdgConfigHome);
|
||||
}
|
||||
|
||||
// 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. 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() reports. Read the working
|
||||
// directory back, because current_path() can resolve a symlink.
|
||||
Path const searchedCwd = current_path();
|
||||
std::string const searchedXdgDir = tc.path() + "/.config/" + systemName();
|
||||
std::string const searchedSystemDir = systemDir.string();
|
||||
|
||||
// Capture stderr around the setup() call only, so the
|
||||
// assertions below keep their own output.
|
||||
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 the directory paths, not the wording of the header.
|
||||
BEAST_EXPECT(reported.contains(searchedCwd.string()));
|
||||
BEAST_EXPECT(reported.contains(searchedXdgDir));
|
||||
BEAST_EXPECT(reported.contains(searchedSystemDir));
|
||||
|
||||
// A run with no config file names no data directory. This call
|
||||
// is standalone, which clears it anyway, so the check states the
|
||||
// result rather than proving the creation step was skipped.
|
||||
BEAST_EXPECT(c.legacy(Sections::kDatabasePath).empty());
|
||||
}
|
||||
}
|
||||
#endif // _MSC_VER
|
||||
|
||||
// Restore the current working directory.
|
||||
@@ -1085,11 +1213,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());
|
||||
@@ -1098,11 +1226,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());
|
||||
@@ -1111,11 +1239,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());
|
||||
@@ -1124,11 +1252,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());
|
||||
@@ -1137,11 +1265,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());
|
||||
@@ -1150,11 +1278,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());
|
||||
@@ -1163,11 +1291,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());
|
||||
@@ -1176,11 +1304,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());
|
||||
|
||||
@@ -18,7 +18,7 @@ target_link_libraries(
|
||||
PRIVATE GTest::gtest GTest::gmock xrpl.libxrpl rpcspec::rpcspec
|
||||
)
|
||||
|
||||
set(test_modules rpc)
|
||||
set(test_modules app rpc)
|
||||
|
||||
foreach(module IN LISTS test_modules)
|
||||
file(
|
||||
|
||||
31
src/tests/xrpld/app/main/Main.cpp
Normal file
31
src/tests/xrpld/app/main/Main.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include <xrpld/app/main/Main.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// A config file that was read is never fatal.
|
||||
|
||||
TEST(MissingConfigIsFatal, read_in_rpc_mode)
|
||||
{
|
||||
EXPECT_FALSE(missingConfigIsFatal(true, true));
|
||||
}
|
||||
|
||||
TEST(MissingConfigIsFatal, read_for_a_server_start)
|
||||
{
|
||||
EXPECT_FALSE(missingConfigIsFatal(true, false));
|
||||
}
|
||||
|
||||
// The search found nothing. The RPC client runs anyway, a server does not.
|
||||
|
||||
TEST(MissingConfigIsFatal, search_found_nothing_in_rpc_mode)
|
||||
{
|
||||
EXPECT_FALSE(missingConfigIsFatal(false, true));
|
||||
}
|
||||
|
||||
TEST(MissingConfigIsFatal, search_found_nothing_for_a_server_start)
|
||||
{
|
||||
EXPECT_TRUE(missingConfigIsFatal(false, false));
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -1,3 +1,5 @@
|
||||
#include <xrpld/app/main/Main.h>
|
||||
|
||||
#include <xrpld/app/main/Application.h>
|
||||
#include <xrpld/core/Config.h>
|
||||
#include <xrpld/core/TimeKeeper.h>
|
||||
@@ -574,9 +576,25 @@ 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"));
|
||||
// A positional argument selects the RPC client, which needs no config
|
||||
// file. --vacuum returns before that branch and does need one.
|
||||
bool const rpcClientMode = vm.contains("parameters") && !vm.contains("vacuum");
|
||||
|
||||
// Setup returns false when the search found no config file, and throws for
|
||||
// one it cannot read or whose contents are invalid. It reports the reason.
|
||||
try
|
||||
{
|
||||
bool const configRead = config->setup(
|
||||
configFile, vm.contains("quiet"), vm.contains("silent"), vm.contains("standalone"));
|
||||
|
||||
if (missingConfigIsFatal(configRead, rpcClientMode))
|
||||
return 1;
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
std::cerr << "xrpld: invalid configuration: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (vm.contains("vacuum"))
|
||||
{
|
||||
|
||||
22
src/xrpld/app/main/Main.h
Normal file
22
src/xrpld/app/main/Main.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Whether the absence of a config file must end the run.
|
||||
*
|
||||
* Config::setup throws for a file it found but could not read, so reaching here
|
||||
* with nothing read means the search found no file at all. That is fatal only
|
||||
* for a server start, because the RPC client runs without one.
|
||||
*
|
||||
* @param configRead Whether setup read a config file.
|
||||
* @param rpcClientMode Whether a positional argument selects the RPC client.
|
||||
* @return True when the run cannot continue.
|
||||
*/
|
||||
[[nodiscard]] inline bool
|
||||
missingConfigIsFatal(bool configRead, bool rpcClientMode)
|
||||
{
|
||||
return !configRead && !rpcClientMode;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -100,17 +100,21 @@ public:
|
||||
[[nodiscard]] std::filesystem::path
|
||||
getDebugLogFile() const;
|
||||
|
||||
private:
|
||||
std::filesystem::path configFile_;
|
||||
|
||||
public:
|
||||
std::filesystem::path configDir;
|
||||
|
||||
private:
|
||||
std::filesystem::path debugLogfile_;
|
||||
|
||||
/**
|
||||
* Read and parse the given config file.
|
||||
*
|
||||
* @param configFile Path of the config file to read.
|
||||
* @throws std::runtime_error When the file cannot be read, or its contents
|
||||
* are invalid.
|
||||
*/
|
||||
void
|
||||
load();
|
||||
load(std::filesystem::path const& configFile);
|
||||
beast::Journal const j_;
|
||||
|
||||
bool quiet_ = false; // Minimize logging verbosity.
|
||||
@@ -330,9 +334,28 @@ public:
|
||||
public:
|
||||
Config();
|
||||
|
||||
/* Be very careful to make sure these bool params
|
||||
are in the right order. */
|
||||
void
|
||||
/**
|
||||
* Set up the configuration and determine the config and data directories.
|
||||
*
|
||||
* When no config file is named, searches the working directory, then the XDG
|
||||
* config directory when HOME is set, then the system config directory, and
|
||||
* takes the first holding a config file under either name.
|
||||
*
|
||||
* 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 found and read. False means the
|
||||
* search found none, and writes the directories it tried to
|
||||
* stderr. The defaults and the SSL context are still set up; only
|
||||
* the data directory is skipped.
|
||||
* @throws std::runtime_error When a config file was found but cannot be
|
||||
* read, when its contents are invalid, or when the data directory
|
||||
* cannot be created.
|
||||
*/
|
||||
[[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>
|
||||
@@ -297,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);
|
||||
@@ -309,95 +310,130 @@ 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.
|
||||
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, paired with its data directory.
|
||||
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.
|
||||
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 holding a config file under either name.
|
||||
// When none does, configFile stays unset.
|
||||
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);
|
||||
}
|
||||
|
||||
// Update default values
|
||||
load();
|
||||
{
|
||||
// load() may have set a new value for the dataDir
|
||||
std::string const dbPath(legacy(Sections::kDatabasePath));
|
||||
if (!dbPath.empty())
|
||||
{
|
||||
dataDir = std::filesystem::path(dbPath);
|
||||
}
|
||||
else if (runStandalone_)
|
||||
|
||||
if (!configFile)
|
||||
{
|
||||
dataDir.clear();
|
||||
// Report every directory searched, even when quiet, since this is
|
||||
// the reason for the failure.
|
||||
std::cerr << std::format(
|
||||
"No {} or {} found. Searched:\n", kConfigFileName, kConfigLegacyName);
|
||||
for (auto const& candidate : candidates)
|
||||
std::cerr << std::format(" {}\n", candidate.configDir.string());
|
||||
}
|
||||
}
|
||||
|
||||
if (!dataDir.empty())
|
||||
// Read the config file, which updates the default values. A file that is
|
||||
// there but cannot be read throws, so getting past this means the file was
|
||||
// read. The rest of this function still runs when the search found none,
|
||||
// so that the command line RPC client gets the defaults and the SSL
|
||||
// context. The caller decides whether that is fatal.
|
||||
bool const configRead = configFile.has_value();
|
||||
|
||||
if (configRead)
|
||||
load(*configFile);
|
||||
|
||||
// Only a config file that was read can name a data directory.
|
||||
if (configRead)
|
||||
{
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories(dataDir, ec);
|
||||
{
|
||||
// load() may have set a new value for the dataDir
|
||||
std::string const dbPath(legacy(Sections::kDatabasePath));
|
||||
if (!dbPath.empty())
|
||||
{
|
||||
dataDir = std::filesystem::path(dbPath);
|
||||
}
|
||||
else if (runStandalone_)
|
||||
{
|
||||
dataDir.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (ec)
|
||||
Throw<std::runtime_error>(std::format("Can not create {}", dataDir.string()));
|
||||
if (!dataDir.empty())
|
||||
{
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories(dataDir, ec);
|
||||
|
||||
legacy(Sections::kDatabasePath, std::filesystem::absolute(dataDir).string());
|
||||
if (ec)
|
||||
{
|
||||
Throw<std::runtime_error>(
|
||||
std::format("Can not create {}: {}", dataDir.string(), ec.message()));
|
||||
}
|
||||
|
||||
legacy(Sections::kDatabasePath, std::filesystem::absolute(dataDir).string());
|
||||
}
|
||||
}
|
||||
|
||||
HTTPClient::initializeSSLContext(this->sslVerifyDir, this->sslVerifyFile, this->sslVerify, j_);
|
||||
@@ -410,6 +446,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 configRead;
|
||||
}
|
||||
|
||||
// 0 ports are allowed for unit tests, but still not allowed to be present in
|
||||
@@ -441,22 +479,25 @@ checkZeroPorts(Config const& config)
|
||||
}
|
||||
|
||||
void
|
||||
Config::load()
|
||||
Config::load(std::filesystem::path const& configFile)
|
||||
{
|
||||
// A std::filesystem::path has no std::formatter, and streaming one adds
|
||||
// quotes of its own.
|
||||
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;
|
||||
Throw<std::runtime_error>(
|
||||
std::format("Failed to read '{}': {} (error {})", fileName, ec.message(), ec.value()));
|
||||
}
|
||||
|
||||
loadFromString(fileContents);
|
||||
|
||||
Reference in New Issue
Block a user