20 #include <ripple/app/main/Application.h>
21 #include <ripple/app/main/DBInit.h>
22 #include <ripple/basics/Log.h>
23 #include <ripple/basics/StringUtilities.h>
24 #include <ripple/basics/contract.h>
25 #include <ripple/beast/clock/basic_seconds_clock.h>
26 #include <ripple/beast/core/CurrentThreadName.h>
27 #include <ripple/core/Config.h>
28 #include <ripple/core/ConfigSections.h>
29 #include <ripple/core/DatabaseCon.h>
30 #include <ripple/core/TimeKeeper.h>
31 #include <ripple/json/to_string.h>
32 #include <ripple/net/RPCCall.h>
33 #include <ripple/protocol/BuildInfo.h>
34 #include <ripple/resource/Fees.h>
35 #include <ripple/rpc/RPCHandler.h>
37 #include <beast/unit_test/match.hpp>
38 #include <test/unit_test/multi_runner.h>
40 #include <google/protobuf/stubs/common.h>
42 #include <boost/filesystem.hpp>
43 #include <boost/predef.h>
44 #include <boost/process.hpp>
45 #include <boost/program_options.hpp>
53 #include <sys/timeb.h>
54 #include <sys/types.h>
59 #if !BOOST_OS_LINUX && !BOOST_OS_WINDOWS && !BOOST_OS_MACOS
60 #error Supported platforms are: Linux, Windows and MacOS
64 #if (BOOST_OS_LINUX && (BOOST_OS_WINDOWS || BOOST_OS_MACOS)) || \
65 (BOOST_OS_MACOS && (BOOST_OS_WINDOWS || BOOST_OS_LINUX)) || \
66 (BOOST_OS_WINDOWS && (BOOST_OS_LINUX || BOOST_OS_MACOS))
67 #error Multiple supported platforms appear active at once
70 namespace po = boost::program_options;
83 if (getrlimit(RLIMIT_NOFILE, &rl) == 0)
86 if (rl.rlim_cur == RLIM_INFINITY)
89 available = rl.rlim_cur;
91 if (available < needed)
98 if (setrlimit(RLIMIT_NOFILE, &rl) == 0)
99 available = rl.rlim_cur;
103 if (needed > available)
105 j.
fatal() <<
"Insufficient number of file descriptors: " << needed
106 <<
" are needed, but only " << available <<
" are available.";
108 std::cerr <<
"Insufficient number of file descriptors: " << needed
109 <<
" are needed, but only " << available
110 <<
" are available.\n";
123 <<
systemName() <<
"d [options] <command> <params>\n"
126 " account_currencies <account> [<ledger>] [strict]\n"
127 " account_info <account>|<seed>|<pass_phrase>|<key> [<ledger>] "
129 " account_lines <account> <account>|\"\" [<ledger>]\n"
130 " account_channels <account> <account>|\"\" [<ledger>]\n"
131 " account_objects <account> [<ledger>] [strict]\n"
132 " account_offers <account>|<account_public_key> [<ledger>] "
134 " account_tx accountID [ledger_min [ledger_max [limit "
135 "[offset]]]] [binary] [count] [descending]\n"
136 " book_offers <taker_pays> <taker_gets> [<taker [<ledger> "
137 "[<limit> [<proof> [<marker>]]]]]\n"
138 " can_delete [<ledgerid>|<ledgerhash>|now|always|never]\n"
139 " channel_authorize <private_key> <channel_id> <drops>\n"
140 " channel_verify <public_key> <channel_id> <drops> <signature>\n"
141 " connect <ip> [<port>]\n"
143 " deposit_authorized <source_account> <destination_account> "
145 " download_shard [[<index> <url>]]\n"
146 " feature [<feature> [accept|reject]]\n"
147 " fetch_info [clear]\n"
148 " gateway_balances [<ledger>] <issuer_account> [ <hotwallet> [ "
151 " json <method> <json>\n"
152 " ledger [<id>|current|closed|validated] [full]\n"
157 " ledger_request <ledger>\n"
158 " log_level [[<partition>] <severity>]\n"
163 " peer_reservations_add <public_key> [<description>]\n"
164 " peer_reservations_del <public_key>\n"
165 " peer_reservations_list\n"
167 " ripple_path_find <json> [<ledger>]\n"
168 " server_info [counters]\n"
169 " server_state [counters]\n"
170 " sign <private_key> <tx_json> [offline]\n"
171 " sign_for <signer_address> <signer_private_key> <tx_json> "
174 " submit <tx_blob>|[<private_key> <tx_json>]\n"
175 " submit_multisigned <tx_json>\n"
177 " validation_create [<seed>|<pass_phrase>|<key>]\n"
179 " validator_list_sites\n"
181 " wallet_propose [<passphrase>]\n";
195 explicit multi_selector(
std::string const& patterns =
"")
198 boost::split(v, patterns, boost::algorithm::is_any_of(
","));
202 if (selectors_.empty() || !s.empty())
203 selectors_.emplace_back(
204 beast::unit_test::selector::automatch, s);
209 operator()(beast::unit_test::suite_info
const& s)
211 for (
auto& sel : selectors_)
239 if (!child && num_jobs == 1)
244 child_runner.arg(argument);
245 auto const any_failed = child_runner.run_multi(multi_selector(pattern));
260 for (
int i = 1; i < argc; ++i)
267 boost::process::exe = exe_name, boost::process::args = args);
269 int bad_child_exits = 0;
270 for (
auto& c : children)
285 if (parent_runner.
any_failed() || bad_child_exits)
293 runner.arg(argument);
294 auto const anyFailed = runner.run_multi(multi_selector(pattern));
305 run(
int argc,
char** argv)
312 po::variables_map vm;
316 importText +=
"Import an existing node database (specified in the [";
318 importText +=
"] configuration file section) into the current ";
319 importText +=
"node database (specified in the [";
321 importText +=
"] configuration file section).";
325 shardsText +=
"Validate an existing shard database (specified in the [";
327 shardsText +=
"] configuration file section).";
332 po::options_description gen(
"General Options");
334 "conf", po::value<std::string>(),
"Specify the configuration file.")(
335 "debug",
"Enable normally suppressed debug logging")(
336 "help,h",
"Display this message.")(
338 po::value<std::size_t>(),
339 "Override the minimum validation quorum.")(
340 "silent",
"No output to the console after startup.")(
341 "standalone,a",
"Run with no peers.")(
"verbose,v",
"Verbose logging.")(
342 "version",
"Display the build version.");
344 po::options_description
data(
"Ledger/Data Options");
345 data.add_options()(
"import", importText.
c_str())(
347 po::value<std::string>(),
348 "Load the specified ledger and start from the value given.")(
350 po::value<std::string>(),
351 "Load the specified ledger file.")(
352 "load",
"Load the current ledger from the local DB.")(
353 "net",
"Get the initial ledger from the network.")(
354 "nodetoshard",
"Import node store into shards")(
355 "replay",
"Replay a ledger close.")(
356 "start",
"Start from a fresh Ledger.")(
357 "vacuum",
"VACUUM the transaction db.")(
358 "valid",
"Consider the initial ledger a valid network ledger.")(
359 "validateShards", shardsText.
c_str());
361 po::options_description rpc(
"RPC Client Options");
364 "Perform rpc command - see below for available commands. "
365 "This is assumed if any positional parameters are provided.")(
367 po::value<std::string>(),
368 "Specify the IP address for RPC command. "
369 "Format: <ip-address>[':'<port-number>]")(
371 po::value<std::uint16_t>(),
372 "DEPRECATED: include with rpc_ip instead. "
373 "Specify the port number for RPC command.");
375 po::options_description test(
"Unit Test Options");
378 "Suppress test suite messages, "
379 "including suite/case name (at start) and test log messages.")(
381 po::value<std::string>()->implicit_value(
""),
382 "Perform unit tests. The optional argument specifies one or "
383 "more comma-separated selectors. Each selector specifies a suite name, "
384 "full-name (lib.module.suite), module, or library "
388 po::value<std::string>()->implicit_value(
""),
389 "Supplies an argument string to unit tests. If provided, this argument "
390 "is made available to each suite that runs. Interpretation of the "
391 "argument is handled individually by any suite that accesses it -- "
392 "as such, it typically only make sense to provide this when running "
395 "Use IPv6 localhost when running unittests (default is IPv4).")(
397 "Force unit test log message output. Only useful in combination with "
398 "--quiet, in which case log messages will print but suite/case names "
401 po::value<std::size_t>(),
402 "Number of unittest jobs to run in parallel (child processes).");
406 po::options_description hidden(
"Hidden Options");
407 hidden.add_options()(
409 po::value<vector<string>>(),
410 "Specify rpc command and parameters. This option must be repeated "
411 "for each command/param. Positional parameters also serve this "
413 "so this option is not needed for users")(
415 "For internal use only when spawning child unit test processes.")(
416 "fg",
"Deprecated: server always in foreground mode.");
419 po::positional_options_description p;
420 p.add(
"parameters", -1);
422 po::options_description all;
423 all.add(gen).add(rpc).add(data).add(test).add(hidden);
425 po::options_description desc;
426 desc.add(gen).add(rpc).add(data).add(test);
432 po::command_line_parser(argc, argv)
446 if (vm.count(
"help"))
452 if (vm.count(
"version"))
462 if (vm.count(
"unittest"))
466 if (vm.count(
"unittest-arg"))
470 bool unittestChild =
false;
471 if (vm.count(
"unittest-jobs"))
472 numJobs =
std::max(numJobs, vm[
"unittest-jobs"].as<std::size_t>());
473 unittestChild = bool(vm.count(
"unittest-child"));
476 vm[
"unittest"].as<std::string>(),
478 bool(vm.count(
"quiet")),
479 bool(vm.count(
"unittest-log")),
481 bool(vm.count(
"unittest-ipv6")),
488 if (vm.count(
"unittest-jobs"))
491 std::cerr <<
"rippled: '--unittest-jobs' specified without "
493 std::cerr <<
"To run the unit tests the '--unittest' option must "
499 auto config = std::make_unique<Config>();
507 bool(vm.count(
"quiet")),
508 bool(vm.count(
"silent")),
509 bool(vm.count(
"standalone")));
511 if (vm.count(
"vacuum"))
513 if (config->standalone())
515 std::cerr <<
"vacuum not applicable in standalone mode.\n";
519 using namespace boost::filesystem;
521 path dbPath = dbSetup.dataDir /
TxDBName;
526 assert(dbSize !=
static_cast<uintmax_t>(-1));
528 if (
auto available =
space(dbPath.parent_path()).available;
531 std::cerr <<
"The database filesystem must have at least as "
532 "much free space as the size of "
533 << dbPath.string() <<
", which is " << dbSize
534 <<
" bytes. Only " << available
535 <<
" bytes are available.\n";
539 auto txnDB = std::make_unique<DatabaseCon>(
541 auto& session = txnDB->getSession();
548 session <<
"PRAGMA page_size;", soci::into(pageSize);
550 std::cout <<
"VACUUM beginning. page_size: " << pageSize
553 session <<
"VACUUM;";
554 assert(dbSetup.globalPragma);
555 for (
auto const& p : *dbSetup.globalPragma)
557 session <<
"PRAGMA page_size;", soci::into(pageSize);
559 std::cout <<
"VACUUM finished. page_size: " << pageSize
564 std::cerr <<
"exception " << e.
what() <<
" in function " << __func__
572 if (vm.count(
"start"))
575 if (vm.count(
"import"))
576 config->doImport =
true;
578 if (vm.count(
"nodetoshard"))
579 config->nodeToShard =
true;
581 if (vm.count(
"validateShards"))
582 config->validateShards =
true;
584 if (vm.count(
"ledger"))
586 config->START_LEDGER = vm[
"ledger"].as<
std::string>();
587 if (vm.count(
"replay"))
592 else if (vm.count(
"ledgerfile"))
594 config->START_LEDGER = vm[
"ledgerfile"].as<
std::string>();
597 else if (vm.count(
"load"))
602 if (vm.count(
"valid"))
604 config->START_VALID =
true;
612 std::cerr <<
"Net and load/replay options are incompatible"
622 if (vm.count(
"rpc_ip"))
625 vm[
"rpc_ip"].as<std::string>());
633 if (endpoint->port() == 0)
635 std::cerr <<
"No port specified in rpc_ip.\n";
636 if (vm.count(
"rpc_port"))
638 std::cerr <<
"WARNING: using deprecated rpc_port param.\n";
642 endpoint->at_port(vm[
"rpc_port"].as<std::uint16_t>());
643 if (endpoint->port() == 0)
656 config->rpc_ip = std::move(*endpoint);
659 if (vm.count(
"quorum"))
663 config->VALIDATION_QUORUM = vm[
"quorum"].as<
std::size_t>();
671 std::cerr <<
"Invalid value specified for --quorum (" << e.
what()
681 if (vm.count(
"quiet"))
683 else if (vm.count(
"verbose"))
686 auto logs = std::make_unique<Logs>(thresh);
689 if (!vm.count(
"parameters"))
693 if (config->had_trailing_comments())
695 JLOG(logs->journal(
"Application").warn())
696 <<
"Trailing comments were seen in your config file. "
697 <<
"The treatment of inline/trailing comments has changed "
699 <<
"Any `#` characters NOT intended to delimit comments should "
701 <<
"preceded by a \\";
709 if (vm.count(
"debug"))
715 std::move(config), std::move(logs), std::move(timeKeeper));
723 app->fdRequired(), app->logs().journal(
"Application")))
746 main(
int argc,
char** argv)
766 atexit(&google::protobuf::ShutdownProtobufLibrary);
768 auto const result(ripple::run(argc, argv));