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/Sustain.h>
25 #include <ripple/basics/contract.h>
26 #include <ripple/beast/clock/basic_seconds_clock.h>
27 #include <ripple/beast/core/CurrentThreadName.h>
28 #include <ripple/core/Config.h>
29 #include <ripple/core/ConfigSections.h>
30 #include <ripple/core/DatabaseCon.h>
31 #include <ripple/core/TimeKeeper.h>
32 #include <ripple/json/to_string.h>
33 #include <ripple/net/RPCCall.h>
34 #include <ripple/protocol/BuildInfo.h>
35 #include <ripple/protocol/digest.h>
36 #include <ripple/resource/Fees.h>
37 #include <ripple/rpc/RPCHandler.h>
39 #include <beast/unit_test/match.hpp>
40 #include <test/unit_test/multi_runner.h>
42 #include <google/protobuf/stubs/common.h>
44 #include <boost/filesystem.hpp>
45 #include <boost/predef.h>
46 #include <boost/process.hpp>
47 #include <boost/program_options.hpp>
55 #include <sys/timeb.h>
56 #include <sys/types.h>
61 #if !BOOST_OS_LINUX && !BOOST_OS_WINDOWS && !BOOST_OS_MACOS
62 #error Supported platforms are: Linux, Windows and MacOS
66 #if (BOOST_OS_LINUX && (BOOST_OS_WINDOWS || BOOST_OS_MACOS)) || \
67 (BOOST_OS_MACOS && (BOOST_OS_WINDOWS || BOOST_OS_LINUX)) || \
68 (BOOST_OS_WINDOWS && (BOOST_OS_LINUX || BOOST_OS_MACOS))
69 #error Multiple supported platforms appear active at once
72 namespace po = boost::program_options;
85 if (getrlimit(RLIMIT_NOFILE, &rl) == 0)
88 if (rl.rlim_cur == RLIM_INFINITY)
91 available = rl.rlim_cur;
93 if (available < needed)
100 if (setrlimit(RLIMIT_NOFILE, &rl) == 0)
101 available = rl.rlim_cur;
105 if (needed > available)
107 j.
fatal() <<
"Insufficient number of file descriptors: " << needed
108 <<
" are needed, but only " << available <<
" are available.";
110 std::cerr <<
"Insufficient number of file descriptors: " << needed
111 <<
" are needed, but only " << available
112 <<
" are available.\n";
125 <<
systemName() <<
"d [options] <command> <params>\n"
128 " account_currencies <account> [<ledger>] [strict]\n"
129 " account_info <account>|<seed>|<pass_phrase>|<key> [<ledger>] "
131 " account_lines <account> <account>|\"\" [<ledger>]\n"
132 " account_channels <account> <account>|\"\" [<ledger>]\n"
133 " account_objects <account> [<ledger>] [strict]\n"
134 " account_offers <account>|<account_public_key> [<ledger>] "
136 " account_tx accountID [ledger_min [ledger_max [limit "
137 "[offset]]]] [binary] [count] [descending]\n"
138 " book_offers <taker_pays> <taker_gets> [<taker [<ledger> "
139 "[<limit> [<proof> [<marker>]]]]]\n"
140 " can_delete [<ledgerid>|<ledgerhash>|now|always|never]\n"
141 " channel_authorize <private_key> <channel_id> <drops>\n"
142 " channel_verify <public_key> <channel_id> <drops> <signature>\n"
143 " connect <ip> [<port>]\n"
145 " deposit_authorized <source_account> <destination_account> "
147 " download_shard [[<index> <url>]]\n"
148 " feature [<feature> [accept|reject]]\n"
149 " fetch_info [clear]\n"
150 " gateway_balances [<ledger>] <issuer_account> [ <hotwallet> [ "
153 " json <method> <json>\n"
154 " ledger [<id>|current|closed|validated] [full]\n"
159 " ledger_request <ledger>\n"
160 " log_level [[<partition>] <severity>]\n"
165 " peer_reservations_add <public_key> [<description>]\n"
166 " peer_reservations_del <public_key>\n"
167 " peer_reservations_list\n"
169 " ripple_path_find <json> [<ledger>]\n"
170 " server_info [counters]\n"
171 " server_state [counters]\n"
172 " sign <private_key> <tx_json> [offline]\n"
173 " sign_for <signer_address> <signer_private_key> <tx_json> "
176 " submit <tx_blob>|[<private_key> <tx_json>]\n"
177 " submit_multisigned <tx_json>\n"
179 " validation_create [<seed>|<pass_phrase>|<key>]\n"
181 " validator_list_sites\n"
183 " wallet_propose [<passphrase>]\n";
197 explicit multi_selector(
std::string const& patterns =
"")
200 boost::split(v, patterns, boost::algorithm::is_any_of(
","));
204 if (selectors_.empty() || !s.empty())
205 selectors_.emplace_back(
206 beast::unit_test::selector::automatch, s);
211 operator()(beast::unit_test::suite_info
const& s)
213 for (
auto& sel : selectors_)
241 if (!child && num_jobs == 1)
246 child_runner.arg(argument);
247 auto const any_failed = child_runner.run_multi(multi_selector(pattern));
262 for (
int i = 1; i < argc; ++i)
269 boost::process::exe = exe_name, boost::process::args = args);
271 int bad_child_exits = 0;
272 for (
auto& c : children)
287 if (parent_runner.
any_failed() || bad_child_exits)
295 runner.arg(argument);
296 auto const anyFailed = runner.run_multi(multi_selector(pattern));
307 run(
int argc,
char** argv)
313 po::variables_map vm;
317 importText +=
"Import an existing node database (specified in the [";
319 importText +=
"] configuration file section) into the current ";
320 importText +=
"node database (specified in the [";
322 importText +=
"] configuration file section).";
326 shardsText +=
"Validate an existing shard database (specified in the [";
328 shardsText +=
"] configuration file section).";
333 po::options_description gen(
"General Options");
335 "conf", po::value<std::string>(),
"Specify the configuration file.")(
336 "debug",
"Enable normally suppressed debug logging")(
337 "fg",
"Run in the foreground.")(
"help,h",
"Display this message.")(
339 po::value<std::size_t>(),
340 "Override the minimum validation quorum.")(
341 "silent",
"No output to the console after startup.")(
342 "standalone,a",
"Run with no peers.")(
"verbose,v",
"Verbose logging.")(
343 "version",
"Display the build version.");
345 po::options_description
data(
"Ledger/Data Options");
346 data.add_options()(
"import", importText.
c_str())(
348 po::value<std::string>(),
349 "Load the specified ledger and start from the value given.")(
351 po::value<std::string>(),
352 "Load the specified ledger file.")(
353 "load",
"Load the current ledger from the local DB.")(
354 "net",
"Get the initial ledger from the network.")(
355 "nodetoshard",
"Import node store into shards")(
356 "replay",
"Replay a ledger close.")(
357 "start",
"Start from a fresh Ledger.")(
359 po::value<std::string>(),
360 "VACUUM the transaction db. Mandatory string argument specifies "
361 "temporary directory path.")(
362 "valid",
"Consider the initial ledger a valid network ledger.")(
363 "validateShards", shardsText.
c_str());
365 po::options_description rpc(
"RPC Client Options");
368 "Perform rpc command - see below for available commands. "
369 "This is assumed if any positional parameters are provided.")(
371 po::value<std::string>(),
372 "Specify the IP address for RPC command. "
373 "Format: <ip-address>[':'<port-number>]")(
375 po::value<std::uint16_t>(),
376 "DEPRECATED: include with rpc_ip instead. "
377 "Specify the port number for RPC command.");
379 po::options_description test(
"Unit Test Options");
382 "Suppress test suite messages, "
383 "including suite/case name (at start) and test log messages.")(
385 po::value<std::string>()->implicit_value(
""),
386 "Perform unit tests. The optional argument specifies one or "
387 "more comma-separated selectors. Each selector specifies a suite name, "
388 "full-name (lib.module.suite), module, or library "
392 po::value<std::string>()->implicit_value(
""),
393 "Supplies an argument string to unit tests. If provided, this argument "
394 "is made available to each suite that runs. Interpretation of the "
395 "argument is handled individually by any suite that accesses it -- "
396 "as such, it typically only make sense to provide this when running "
399 "Use IPv6 localhost when running unittests (default is IPv4).")(
401 "Force unit test log message output. Only useful in combination with "
402 "--quiet, in which case log messages will print but suite/case names "
405 po::value<std::size_t>(),
406 "Number of unittest jobs to run in parallel (child processes).");
410 po::options_description hidden(
"Hidden Options");
411 hidden.add_options()(
413 po::value<vector<string>>(),
414 "Specify rpc command and parameters. This option must be repeated "
415 "for each command/param. Positional parameters also serve this "
417 "so this option is not needed for users")(
419 "For internal use only when spawning child unit test processes.");
422 po::positional_options_description p;
423 p.add(
"parameters", -1);
425 po::options_description all;
426 all.add(gen).add(rpc).add(data).add(test).add(hidden);
428 po::options_description desc;
429 desc.add(gen).add(rpc).add(data).add(test);
435 po::command_line_parser(argc, argv)
449 if (vm.count(
"help"))
455 if (vm.count(
"version"))
465 if (vm.count(
"unittest"))
469 if (vm.count(
"unittest-arg"))
473 bool unittestChild =
false;
474 if (vm.count(
"unittest-jobs"))
475 numJobs =
std::max(numJobs, vm[
"unittest-jobs"].as<std::size_t>());
476 unittestChild = bool(vm.count(
"unittest-child"));
479 vm[
"unittest"].as<std::string>(),
481 bool(vm.count(
"quiet")),
482 bool(vm.count(
"unittest-log")),
484 bool(vm.count(
"unittest-ipv6")),
491 if (vm.count(
"unittest-jobs"))
494 std::cerr <<
"rippled: '--unittest-jobs' specified without "
496 std::cerr <<
"To run the unit tests the '--unittest' option must "
502 auto config = std::make_unique<Config>();
510 bool(vm.count(
"quiet")),
511 bool(vm.count(
"silent")),
512 bool(vm.count(
"standalone")));
514 if (vm.count(
"vacuum"))
516 if (config->standalone())
518 std::cerr <<
"vacuum not applicable in standalone mode.\n";
522 using namespace boost::filesystem;
524 path dbPath = dbSetup.dataDir /
TxDBName;
530 assert(dbSize !=
static_cast<uintmax_t>(-1));
532 if (
space(tmpPath).available < dbSize)
534 std::cerr <<
"A valid directory for vacuuming must be "
535 "specified on a filesystem with at least "
536 "as much free space as the size of "
537 << dbPath.string() <<
", which is " << dbSize
538 <<
" bytes. The filesystem for " << tmpPath.string()
539 <<
" only has " <<
space(tmpPath).available
544 auto txnDB = std::make_unique<DatabaseCon>(
546 auto& session = txnDB->getSession();
549 session <<
"PRAGMA page_size;", soci::into(pageSize);
551 std::cout <<
"VACUUM beginning. page_size: " << pageSize
554 session <<
"PRAGMA journal_mode=OFF;";
555 session <<
"PRAGMA temp_store_directory=\"" << tmpPath.string()
557 session <<
"VACUUM;";
558 session <<
"PRAGMA journal_mode=WAL;";
559 session <<
"PRAGMA page_size;", soci::into(pageSize);
561 std::cout <<
"VACUUM finished. page_size: " << pageSize
566 std::cerr <<
"exception " << e.
what() <<
" in function " << __func__
574 if (vm.count(
"start"))
577 if (vm.count(
"import"))
578 config->doImport =
true;
580 if (vm.count(
"nodetoshard"))
581 config->nodeToShard =
true;
583 if (vm.count(
"validateShards"))
584 config->validateShards =
true;
586 if (vm.count(
"ledger"))
588 config->START_LEDGER = vm[
"ledger"].as<
std::string>();
589 if (vm.count(
"replay"))
594 else if (vm.count(
"ledgerfile"))
596 config->START_LEDGER = vm[
"ledgerfile"].as<
std::string>();
599 else if (vm.count(
"load"))
604 if (vm.count(
"valid"))
606 config->START_VALID =
true;
614 std::cerr <<
"Net and load/reply options are incompatible"
624 if (vm.count(
"rpc_ip"))
627 vm[
"rpc_ip"].as<std::string>());
635 if (endpoint->port() == 0)
637 std::cerr <<
"No port specified in rpc_ip.\n";
638 if (vm.count(
"rpc_port"))
640 std::cerr <<
"WARNING: using deprecated rpc_port param.\n";
644 endpoint->at_port(vm[
"rpc_port"].as<std::uint16_t>());
645 if (endpoint->port() == 0)
658 config->rpc_ip = std::move(*endpoint);
661 if (vm.count(
"quorum"))
665 config->VALIDATION_QUORUM = vm[
"quorum"].as<
std::size_t>();
673 std::cerr <<
"Invalid value specified for --quorum (" << e.
what()
683 if (vm.count(
"quiet"))
685 else if (vm.count(
"verbose"))
688 auto logs = std::make_unique<Logs>(thresh);
691 if (!vm.count(
"parameters"))
695 if (config->had_trailing_comments())
697 JLOG(logs->journal(
"Application").warn())
698 <<
"Trailing comments were seen in your config file. "
699 <<
"The treatment of inline/trailing comments has changed "
701 <<
"Any `#` characters NOT intended to delimit comments should "
703 <<
"preceded by a \\";
711 if (
HaveSustain() && !vm.count(
"fg") && !config->standalone())
719 if (vm.count(
"debug"))
727 std::move(config), std::move(logs), std::move(timeKeeper));
738 app->fdRequired(), app->logs().journal(
"Application")))
764 main(
int argc,
char** argv)
785 atexit(&google::protobuf::ShutdownProtobufLibrary);
787 auto const result(ripple::run(argc, argv));