//------------------------------------------------------------------------------ /* Copyright (c) 2011-2013, OpenCoin, Inc. */ //============================================================================== namespace po = boost::program_options; void setupServer () { #ifdef RLIMIT_NOFILE struct rlimit rl; if (getrlimit(RLIMIT_NOFILE, &rl) == 0) { if (rl.rlim_cur != rl.rlim_max) { rl.rlim_cur = rl.rlim_max; setrlimit(RLIMIT_NOFILE, &rl); } } #endif getApp().setup (); } void startServer () { // // Execute start up rpc commands. // if (getConfig ().RPC_STARTUP.isArray ()) { for (int i = 0; i != getConfig ().RPC_STARTUP.size (); ++i) { const Json::Value& jvCommand = getConfig ().RPC_STARTUP[i]; if (!getConfig ().QUIET) Log::out() << "Startup RPC: " << jvCommand; RPCHandler rhHandler (&getApp().getOPs ()); // VFALCO TODO Clean up this magic number LoadType loadType = LT_RPCReference; Json::Value jvResult = rhHandler.doCommand (jvCommand, RPCHandler::ADMIN, &loadType); if (!getConfig ().QUIET) Log::out() << "Result: " << jvResult; } } getApp().run (); // Blocks till we get a stop RPC. } void printHelp (const po::options_description& desc) { using namespace std; cerr << SYSTEM_NAME "d [options] " << endl; cerr << desc << endl; cerr << "Commands: " << endl; cerr << " account_info |||| [] [strict]" << endl; cerr << " account_lines |\"\" []" << endl; cerr << " account_offers || []" << endl; cerr << " account_tx accountID [ledger_min [ledger_max [limit [offset]]]] [binary] [count] [descending]" << endl; cerr << " book_offers [ [ [ []]]]]" << endl; cerr << " connect []" << endl; cerr << " consensus_info" << endl; #if ENABLE_INSECURE cerr << " data_delete " << endl; cerr << " data_fetch " << endl; cerr << " data_store " << endl; #endif cerr << " get_counts" << endl; cerr << " json " << endl; cerr << " ledger [|current|closed|validated] [full]" << endl; cerr << " ledger_accept" << endl; cerr << " ledger_closed" << endl; cerr << " ledger_current" << endl; cerr << " ledger_header " << endl; cerr << " logrotate " << endl; cerr << " peers" << endl; cerr << " proof_create [] []" << endl; cerr << " proof_solve " << endl; cerr << " proof_verify [] []" << endl; cerr << " random" << endl; cerr << " ripple ..." << endl; cerr << " ripple_path_find []" << endl; // cerr << " send [] [] []" << endl; cerr << " stop" << endl; cerr << " tx " << endl; cerr << " unl_add | []" << endl; cerr << " unl_delete |" << endl; cerr << " unl_list" << endl; cerr << " unl_load" << endl; cerr << " unl_network" << endl; cerr << " unl_reset" << endl; cerr << " validation_create [||]" << endl; cerr << " validation_seed [||]" << endl; cerr << " wallet_add [] []" << endl; cerr << " wallet_accounts " << endl; cerr << " wallet_claim [] []" << endl; cerr << " wallet_seed [||]" << endl; cerr << " wallet_propose []" << endl; // Transaction helpers (that were removed): // cerr << " account_domain_set []" << endl; // cerr << " account_email_set []" << endl; // cerr << " account_rate_set " << endl; // cerr << " account_wallet_set []" << endl; // cerr << " nickname_info " << endl; // cerr << " nickname_set [] []" << endl; // cerr << " offer_create [passive]" << endl; // cerr << " offer_cancel " << endl; // cerr << " password_fund []" << endl; // cerr << " password_set []" << endl; // cerr << " trust_set [] []" << endl; } //------------------------------------------------------------------------------ // OUr custom unit test runner // class RippleUnitTests : public UnitTests { public: explicit RippleUnitTests (bool shouldLog) : m_shouldLog (shouldLog) { // VFALCO NOTE It sucks that we have to do this but some // code demands the Application object exists. // // TODO To find out who, just change the #if 1 to #if 0 #if 1 setupConfigForUnitTests (&getConfig ()); getApp (); #endif setAssertOnFailure (false); } void logMessage (String const& message) { if (m_shouldLog) { #if BEAST_MSVC if (beast_isRunningUnderDebugger ()) { Logger::outputDebugString (message); } else { std::cout << message.toStdString () << std::endl; } #else std::cout << message.toStdString () << std::endl; #endif } } private: void setupConfigForUnitTests (Config* config) { config->nodeDatabase = parseDelimitedKeyValueString ("type=memory"); config->ephemeralNodeDatabase = StringPairArray (); config->importNodeDatabase = StringPairArray (); } private: bool const m_shouldLog; }; static int runUnitTests (String const& whichTests, String const& format) { bool const shouldLog = format != "junit"; if (format != "junit" && format != "text" && format != "") { String s; s << "Warning, unknown unittest-format='" << format << "'"; Log::out () << s.toStdString (); } RippleUnitTests tr (shouldLog); if (whichTests == "") { tr.runAllTests (); } else { tr.runTestsByName (whichTests); } if (format == "junit") { UnitTestUtilities::JUnitXMLFormatter f (tr); String const s = f.createDocumentString (); std::cout << s.toStdString (); } else { UnitTests::Results const& r (tr.getResults ()); String s; s << "Summary: " << String (r.suites.size ()) << " suites, " << String (r.cases) << " cases, " << String (r.tests) << " tests, " << String (r.failures) << " failure" << ((r.failures != 1) ? "s" : "") << "."; tr.logMessage (s); } return tr.anyTestsFailed () ? EXIT_FAILURE : EXIT_SUCCESS; } //------------------------------------------------------------------------------ int rippleMain (int argc, char** argv) { // // These debug heap calls do nothing in release or non Visual Studio builds. // // Checks the heap at every allocation and deallocation (slow). // //Debug::setAlwaysCheckHeap (false); // Keeps freed memory blocks and fills them with a guard value. // //Debug::setHeapDelayedFree (false); // At exit, reports all memory blocks which have not been freed. // #if 1 Debug::setHeapReportLeaks (false); #else // This is some temporary leak checking test code // Debug::setHeapReportLeaks (false); //malloc (512); // Any leaks before this line in the output are from static initializations. ThreadWithCallQueue t ("test"); GlobalPagedFreeStore::getInstance (); t.start (); return 0; #endif using namespace std; setCallingThreadName ("main"); int iResult = 0; po::variables_map vm; // Map of options. String importDescription; { importDescription << "Import an existing node database (specified in the " << "[" << ConfigSection::importNodeDatabase () << "] configuration file section) " "into the current node database (specified in the " << "[" << ConfigSection::nodeDatabase () << "] configuration file section). "; } // VFALCO TODO Replace boost program options with something from Beast. // // Set up option parsing. // po::options_description desc ("General Options"); desc.add_options () ("help,h", "Display this message.") ("conf", po::value (), "Specify the configuration file.") ("rpc", "Perform rpc command (default).") ("rpc_ip", po::value (), "Specify the IP address for RPC command. Format: [':']") ("rpc_port", po::value (), "Specify the port number for RPC command.") ("standalone,a", "Run with no peers.") ("testnet,t", "Run in test net mode.") ("unittest,u", po::value ()->implicit_value (""), "Perform unit tests.") ("unittest-format", po::value ()->implicit_value ("text"), "Format unit test output. Choices are 'text', 'junit'") ("parameters", po::value< vector > (), "Specify comma separated parameters.") ("quiet,q", "Reduce diagnotics.") ("verbose,v", "Verbose logging.") ("load", "Load the current ledger from the local DB.") ("replay","Replay a ledger close.") ("ledger", po::value (), "Load the specified ledger and start from .") ("start", "Start from a fresh Ledger.") ("net", "Get the initial ledger from the network.") ("fg", "Run in the foreground.") ("import", importDescription.toStdString ().c_str ()) ; // Interpret positional arguments as --parameters. po::positional_options_description p; p.add ("parameters", -1); // These must be added before the Application object is created NodeStore::addBackendFactory (KeyvaDBBackendFactory::getInstance ()); NodeStore::addBackendFactory (LevelDBBackendFactory::getInstance ()); NodeStore::addBackendFactory (MemoryBackendFactory::getInstance ()); NodeStore::addBackendFactory (NullBackendFactory::getInstance ()); NodeStore::addBackendFactory (SqliteBackendFactory::getInstance ()); #if RIPPLE_HYPERLEVELDB_AVAILABLE NodeStore::addBackendFactory (HyperLevelDBBackendFactory::getInstance ()); #endif #if RIPPLE_MDB_AVAILABLE NodeStore::addBackendFactory (MdbBackendFactory::getInstance ()); #endif if (! RandomNumbers::getInstance ().initialize ()) { Log::out() << "Unable to add system entropy"; iResult = 2; } if (iResult) { nothing (); } else { // Parse options, if no error. try { po::store (po::command_line_parser (argc, argv) .options (desc) // Parse options. .positional (p) // Remainder as --parameters. .run (), vm); po::notify (vm); // Invoke option notify functions. } catch (...) { iResult = 1; } } if (iResult) { nothing (); } else if (vm.count ("help")) { iResult = 1; } // Use a watchdog process unless we're invoking a stand alone type of mode // if (HaveSustain () && !iResult && !vm.count ("parameters") && !vm.count ("fg") && !vm.count ("standalone") && !vm.count ("unittest")) { std::string logMe = DoSustain (getConfig ().DEBUG_LOGFILE.string()); if (!logMe.empty ()) Log (lsWARNING) << logMe; } if (vm.count ("quiet")) { Log::setMinSeverity (lsFATAL, true); } else if (vm.count ("verbose")) { Log::setMinSeverity (lsTRACE, true); } else { Log::setMinSeverity (lsINFO, true); } // Run the unit tests if requested. // The unit tests will exit the application with an appropriate return code. // if (vm.count ("unittest")) { String format; if (vm.count ("unittest-format")) format = vm ["unittest-format"].as (); return runUnitTests (vm ["unittest"].as (), format); } if (!iResult) { getConfig ().setup ( vm.count ("conf") ? vm["conf"].as () : "", // Config file. !!vm.count ("testnet"), // Testnet flag. !!vm.count ("quiet")); // Quiet flag. if (vm.count ("standalone")) { getConfig ().RUN_STANDALONE = true; getConfig ().LEDGER_HISTORY = 0; } } if (vm.count ("start")) getConfig ().START_UP = Config::FRESH; // Handle a one-time import option // if (vm.count ("import")) { String const optionString (vm ["import"].as ()); getConfig ().importNodeDatabase = parseDelimitedKeyValueString (optionString); } if (vm.count ("ledger")) { getConfig ().START_LEDGER = vm["ledger"].as (); if (vm.count("replay")) getConfig ().START_UP = Config::REPLAY; else getConfig ().START_UP = Config::LOAD; } else if (vm.count ("load")) { getConfig ().START_UP = Config::LOAD; } else if (vm.count ("net")) { getConfig ().START_UP = Config::NETWORK; if (getConfig ().VALIDATION_QUORUM < 2) getConfig ().VALIDATION_QUORUM = 2; } if (iResult == 0) { // These overrides must happen after the config file is loaded. // Override the RPC destination IP address // if (vm.count ("rpc_ip")) { getConfig ().setRpcIpAndOptionalPort (vm ["rpc_ip"].as ()); } // Override the RPC destination port number // if (vm.count ("rpc_port")) { // VFALCO TODO This should be a short. getConfig ().setRpcPort (vm ["rpc_port"].as ()); } } if (iResult == 0) { if (!vm.count ("parameters")) { // No arguments. Run server. setupServer (); setCallingThreadName ("io"); startServer (); } else { // Have a RPC command. setCallingThreadName ("rpc"); std::vector vCmd = vm["parameters"].as > (); iResult = commandLineRPC (vCmd); } } if (1 == iResult && !vm.count ("quiet")) printHelp (desc); return iResult; }