20 #include <ripple/basics/FileUtilities.h>
21 #include <ripple/basics/Log.h>
22 #include <ripple/basics/StringUtilities.h>
23 #include <ripple/basics/contract.h>
24 #include <ripple/beast/core/LexicalCast.h>
25 #include <ripple/core/Config.h>
26 #include <ripple/core/ConfigSections.h>
27 #include <ripple/json/json_reader.h>
28 #include <ripple/net/HTTPClient.h>
29 #include <ripple/protocol/Feature.h>
30 #include <ripple/protocol/SystemParameters.h>
31 #include <boost/algorithm/string.hpp>
32 #include <boost/format.hpp>
33 #include <boost/predef.h>
34 #include <boost/regex.hpp>
35 #include <boost/system/error_code.hpp>
43 #include <sysinfoapi.h>
51 if (MEMORYSTATUSEX msx{
sizeof(MEMORYSTATUSEX)}; GlobalMemoryStatusEx(&msx))
62 #include <sys/sysinfo.h>
72 if (sysinfo(&si) == 0)
84 #include <sys/sysctl.h>
85 #include <sys/types.h>
93 int mib[] = {CTL_HW, HW_MEMSIZE};
95 size_t size =
sizeof(ram);
97 if (sysctl(mib, 2, &ram, &size, NULL, 0) == 0)
137 []() constexpr->bool {
138 std::underlying_type_t<SizedItem> idx = 0;
140 for (auto const& i : sizedItems)
142 if (static_cast<std::underlying_type_t<SizedItem>>(i.first) != idx)
150 "Mismatch between sized item enum & array indices");
157 #define SECTION_DEFAULT_NAME ""
167 boost::algorithm::replace_all(strData,
"\r\n",
"\n");
170 boost::algorithm::replace_all(strData,
"\r",
"\n");
172 boost::algorithm::split(vLines, strData, boost::algorithm::is_any_of(
"\n"));
178 secResult[strSection] = IniFileSections::mapped_type();
181 for (
auto& strValue : vLines)
184 boost::algorithm::trim(strValue);
186 if (strValue.empty() || strValue[0] ==
'#')
190 else if (strValue[0] ==
'[' && strValue[strValue.length() - 1] ==
']')
193 strSection = strValue.
substr(1, strValue.length() - 2);
194 secResult.
emplace(strSection, IniFileSections::mapped_type{});
199 if (!strValue.empty())
200 secResult[strSection].push_back(strValue);
207 IniFileSections::mapped_type*
210 IniFileSections::iterator it;
211 IniFileSections::mapped_type* smtResult;
212 it = secSource.
find(strSection);
213 if (it == secSource.
end())
216 smtResult = &(it->second);
227 IniFileSections::mapped_type* pmtEntries =
229 bool bSingle = pmtEntries && 1 == pmtEntries->size();
233 strValue = (*pmtEntries)[0];
237 JLOG(j.
warn()) << boost::str(
238 boost::format(
"Section [%s]: requires 1 line not %d lines.") %
239 strSection % pmtEntries->
size());
251 char const*
const Config::configFileName =
"rippled.cfg";
252 char const*
const Config::databaseDirName =
"db";
253 char const*
const Config::validatorsFileName =
"validators.txt";
260 if (
auto const v =
std::getenv(name); v !=
nullptr)
266 constexpr FeeUnit32 Config::TRANSACTION_FEE_BASE;
269 : j_(
beast::Journal::getNullSink()), ramSize_(detail::getMemorySize())
278 QUIET = bQuiet || bSilent;
287 auto const& threshold =
291 threshold.second.begin(),
292 threshold.second.end(),
294 return (ramSize_ / (1024 * 1024 * 1024)) < limit;
297 if (ns != threshold.second.end())
322 boost::filesystem::path dataDir;
335 if (!strConf.
empty())
336 strConfFile = strConf;
340 if (!strConf.
empty())
350 CONFIG_DIR = boost::filesystem::current_path();
357 auto strXdgConfigHome =
getEnvVar(
"XDG_CONFIG_HOME");
358 auto strXdgDataHome =
getEnvVar(
"XDG_DATA_HOME");
362 || (strHome.empty() &&
363 (strXdgConfigHome.empty() || strXdgDataHome.empty())))
369 if (strXdgConfigHome.empty())
372 strXdgConfigHome = strHome +
"/.config";
375 if (strXdgDataHome.empty())
378 strXdgDataHome = strHome +
"/.local/share";
383 dataDir = strXdgDataHome +
"/" +
systemName();
405 dataDir = boost::filesystem::path(dbPath);
410 if (!dataDir.empty())
412 boost::system::error_code ec;
413 boost::filesystem::create_directories(dataDir, ec);
416 Throw<std::runtime_error>(
417 boost::str(boost::format(
"Can not create %s") % dataDir));
419 legacy(
"database_path", boost::filesystem::absolute(dataDir).
string());
444 boost::system::error_code ec;
477 boost::filesystem::path p(dbPath);
478 legacy(
"database_path", boost::filesystem::absolute(p).
string());
489 PEERS_MAX = beast::lexicalCastThrow<std::size_t>(strTemp);
496 peers_in_max = beast::lexicalCastThrow<std::size_t>(strTemp);
497 if (*peers_in_max > 1000)
498 Throw<std::runtime_error>(
499 "Invalid value specified in [" SECTION_PEERS_IN_MAX
500 "] section; the value must be less or equal than 1000");
506 peers_out_max = beast::lexicalCastThrow<std::size_t>(strTemp);
507 if (*peers_out_max < 10 || *peers_out_max > 1000)
508 Throw<std::runtime_error>(
509 "Invalid value specified in [" SECTION_PEERS_OUT_MAX
510 "] section; the value must be in range 10-1000");
514 if ((peers_in_max && !peers_out_max) ||
515 (peers_out_max && !peers_in_max))
516 Throw<std::runtime_error>(
"Both sections [" SECTION_PEERS_IN_MAX
518 "and [" SECTION_PEERS_OUT_MAX
519 "] must be configured");
521 if (peers_in_max && peers_out_max)
530 if (boost::iequals(strTemp,
"tiny"))
532 else if (boost::iequals(strTemp,
"small"))
534 else if (boost::iequals(strTemp,
"medium"))
536 else if (boost::iequals(strTemp,
"large"))
538 else if (boost::iequals(strTemp,
"huge"))
542 4, beast::lexicalCastThrow<std::size_t>(strTemp));
549 ELB_SUPPORT = beast::lexicalCastThrow<bool>(strTemp);
555 SSL_VERIFY = beast::lexicalCastThrow<bool>(strTemp);
559 if (boost::iequals(strTemp,
"all"))
561 else if (boost::iequals(strTemp,
"trusted"))
563 else if (boost::iequals(strTemp,
"drop_untrusted"))
566 Throw<std::runtime_error>(
567 "Invalid value specified in [" SECTION_RELAY_VALIDATIONS
573 if (boost::iequals(strTemp,
"all"))
575 else if (boost::iequals(strTemp,
"trusted"))
577 else if (boost::iequals(strTemp,
"drop_untrusted"))
580 Throw<std::runtime_error>(
581 "Invalid value specified in [" SECTION_RELAY_PROPOSALS
585 if (
exists(SECTION_VALIDATION_SEED) &&
exists(SECTION_VALIDATOR_TOKEN))
586 Throw<std::runtime_error>(
"Cannot have both [" SECTION_VALIDATION_SEED
587 "] and [" SECTION_VALIDATOR_TOKEN
588 "] config sections");
600 FEE_DEFAULT = beast::lexicalCastThrow<std::uint64_t>(strTemp);
604 if (boost::iequals(strTemp,
"full"))
607 else if (boost::iequals(strTemp,
"none"))
615 if (boost::iequals(strTemp,
"none"))
617 else if (boost::iequals(strTemp,
"full"))
620 FETCH_DEPTH = beast::lexicalCastThrow<std::uint32_t>(strTemp);
628 if (
exists(SECTION_VALIDATION_SEED) ||
exists(SECTION_VALIDATOR_TOKEN))
634 PATH_SEARCH = beast::lexicalCastThrow<int>(strTemp);
647 if (SWEEP_INTERVAL < 10 || SWEEP_INTERVAL > 600)
648 Throw<std::runtime_error>(
"Invalid " SECTION_SWEEP_INTERVAL
649 ": must be between 10 and 600 inclusive");
654 WORKERS = beast::lexicalCastThrow<int>(strTemp);
656 if (WORKERS < 1 || WORKERS > 1024)
657 Throw<std::runtime_error>(
658 "Invalid " SECTION_WORKERS
659 ": must be between 1 and 1024 inclusive.");
664 IO_WORKERS = beast::lexicalCastThrow<int>(strTemp);
666 if (IO_WORKERS < 1 || IO_WORKERS > 1024)
667 Throw<std::runtime_error>(
668 "Invalid " SECTION_IO_WORKERS
669 ": must be between 1 and 1024 inclusive.");
676 if (PREFETCH_WORKERS < 1 || PREFETCH_WORKERS > 1024)
677 Throw<std::runtime_error>(
678 "Invalid " SECTION_PREFETCH_WORKERS
679 ": must be between 1 and 1024 inclusive.");
683 COMPRESSION = beast::lexicalCastThrow<bool>(strTemp);
688 if (
exists(SECTION_REDUCE_RELAY))
690 auto sec =
section(SECTION_REDUCE_RELAY);
697 if (TX_RELAY_PERCENTAGE < 10 || TX_RELAY_PERCENTAGE > 100 ||
699 Throw<std::runtime_error>(
700 "Invalid " SECTION_REDUCE_RELAY
701 ", tx_min_peers must be greater or equal to 10"
702 ", tx_relay_percentage must be greater or equal to 10 "
703 "and less or equal to 100");
709 beast::lexicalCastThrow<int>(strTemp),
718 Throw<std::runtime_error>(
719 "Invalid " SECTION_SERVER_DOMAIN
720 ": the domain name does not appear to meet the requirements.");
726 if (
exists(SECTION_OVERLAY))
728 auto const sec =
section(SECTION_OVERLAY);
734 if (
auto val = sec.get(
"max_unknown_time"))
736 seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
740 Throw<std::runtime_error>(
741 "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
742 ": must be of the form '<number>' representing seconds.");
746 Throw<std::runtime_error>(
747 "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
748 ": the time must be between 300 and 1800 seconds, inclusive.");
752 if (
auto val = sec.get(
"max_diverged_time"))
754 seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
758 Throw<std::runtime_error>(
759 "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
760 ": must be of the form '<number>' representing seconds.");
765 Throw<std::runtime_error>(
766 "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
767 ": the time must be between 60 and 900 seconds, inclusive.");
772 secConfig, SECTION_AMENDMENT_MAJORITY_TIME, strTemp,
j_))
775 boost::regex
const re(
776 "^\\s*(\\d+)\\s*(minutes|hours|days|weeks)\\s*(\\s+.*)?$");
778 if (!boost::regex_match(strTemp, match, re))
779 Throw<std::runtime_error>(
780 "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
781 ", must be: [0-9]+ [minutes|hours|days|weeks]");
784 beast::lexicalCastThrow<std::uint32_t>(match[1].str());
786 if (boost::iequals(match[2],
"minutes"))
788 else if (boost::iequals(match[2],
"hours"))
790 else if (boost::iequals(match[2],
"days"))
792 else if (boost::iequals(match[2],
"weeks"))
796 Throw<std::runtime_error>(
797 "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
798 ", the minimum amount of time an amendment must hold a "
799 "majority is 15 minutes");
816 boost::filesystem::path validatorsFile;
820 validatorsFile = strTemp;
822 if (validatorsFile.empty())
823 Throw<std::runtime_error>(
824 "Invalid path specified in [" SECTION_VALIDATORS_FILE
"]");
826 if (!validatorsFile.is_absolute() && !
CONFIG_DIR.empty())
829 if (!boost::filesystem::exists(validatorsFile))
830 Throw<std::runtime_error>(
831 "The file specified in [" SECTION_VALIDATORS_FILE
834 validatorsFile.string());
837 !boost::filesystem::is_regular_file(validatorsFile) &&
838 !boost::filesystem::is_symlink(validatorsFile))
839 Throw<std::runtime_error>(
840 "Invalid file specified in [" SECTION_VALIDATORS_FILE
842 validatorsFile.string());
848 if (!validatorsFile.empty())
850 if (!boost::filesystem::exists(validatorsFile))
851 validatorsFile.clear();
853 !boost::filesystem::is_regular_file(validatorsFile) &&
854 !boost::filesystem::is_symlink(validatorsFile))
855 validatorsFile.clear();
859 if (!validatorsFile.empty() &&
860 boost::filesystem::exists(validatorsFile) &&
861 (boost::filesystem::is_regular_file(validatorsFile) ||
862 boost::filesystem::is_symlink(validatorsFile)))
864 boost::system::error_code ec;
868 Throw<std::runtime_error>(
869 "Failed to read '" + validatorsFile.string() +
"'." +
886 auto valSiteEntries =
890 section(SECTION_VALIDATOR_LIST_SITES).
append(*valSiteEntries);
898 if (!entries && !valKeyEntries && !valListKeys)
899 Throw<std::runtime_error>(
900 "The file specified in [" SECTION_VALIDATORS_FILE
902 "does not contain a [" SECTION_VALIDATORS
904 "[" SECTION_VALIDATOR_KEYS
906 "[" SECTION_VALIDATOR_LIST_KEYS
909 validatorsFile.string());
916 if (!
section(SECTION_VALIDATOR_LIST_SITES).lines().empty() &&
917 section(SECTION_VALIDATOR_LIST_KEYS).lines().empty())
919 Throw<std::runtime_error>(
921 "] config section is missing");
926 auto const part =
section(
"features");
927 for (
auto const& s : part.values())
932 Throw<std::runtime_error>(
933 "Unknown feature: " + s +
" in config file.");
949 Throw<std::runtime_error>(
950 "The minimum number of required peers (network_quorum) exceeds "
951 "the maximum number of allowed peers (peers_max)");
956 boost::filesystem::path
961 if (!log_file.empty() && !log_file.is_absolute())
965 log_file = boost::filesystem::absolute(log_file,
CONFIG_DIR);
968 if (!log_file.empty())
970 auto log_dir = log_file.parent_path();
972 if (!boost::filesystem::is_directory(log_dir))
974 boost::system::error_code ec;
975 boost::filesystem::create_directories(log_dir, ec);
981 std::cerr <<
"Unable to create log file path " << log_dir
982 <<
": " << ec.message() <<
'\n';
995 assert(!node || *node <= 4);