diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bench/bench_bitcoin.cpp | 10 | ||||
-rw-r--r-- | src/bitcoin-cli.cpp | 17 | ||||
-rw-r--r-- | src/bitcoin-tx.cpp | 10 | ||||
-rw-r--r-- | src/bitcoind.cpp | 13 | ||||
-rw-r--r-- | src/init.cpp | 17 | ||||
-rw-r--r-- | src/interfaces/node.cpp | 6 | ||||
-rw-r--r-- | src/interfaces/node.h | 4 | ||||
-rw-r--r-- | src/qt/bitcoin.cpp | 37 | ||||
-rw-r--r-- | src/test/getarg_tests.cpp | 16 | ||||
-rw-r--r-- | src/test/util_tests.cpp | 67 | ||||
-rw-r--r-- | src/util.cpp | 131 | ||||
-rw-r--r-- | src/util.h | 34 |
12 files changed, 271 insertions, 91 deletions
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp index 1a2671e8d1..a820d67fa8 100644 --- a/src/bench/bench_bitcoin.cpp +++ b/src/bench/bench_bitcoin.cpp @@ -33,13 +33,21 @@ static void SetupBenchArgs() gArgs.AddArg("-plot-plotlyurl=<uri>", strprintf("URL to use for plotly.js (default: %s)", DEFAULT_PLOT_PLOTLYURL), false, OptionsCategory::OPTIONS); gArgs.AddArg("-plot-width=<x>", strprintf("Plot width in pixel (default: %u)", DEFAULT_PLOT_WIDTH), false, OptionsCategory::OPTIONS); gArgs.AddArg("-plot-height=<x>", strprintf("Plot height in pixel (default: %u)", DEFAULT_PLOT_HEIGHT), false, OptionsCategory::OPTIONS); + + // Hidden + gArgs.AddArg("-h", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-help", "", false, OptionsCategory::HIDDEN); } int main(int argc, char** argv) { SetupBenchArgs(); - gArgs.ParseParameters(argc, argv); + std::string error; + if (!gArgs.ParseParameters(argc, argv, error)) { + fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str()); + return false; + } if (HelpRequested(gArgs)) { std::cout << gArgs.GetHelpMessage(); diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 84c0325014..be5ce14480 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -42,6 +42,7 @@ static void SetupCliArgs() gArgs.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), false, OptionsCategory::OPTIONS); gArgs.AddArg("-rpcclienttimeout=<n>", strprintf("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)", DEFAULT_HTTP_CLIENT_TIMEOUT), false, OptionsCategory::OPTIONS); gArgs.AddArg("-rpcconnect=<ip>", strprintf("Send commands to node running on <ip> (default: %s)", DEFAULT_RPCCONNECT), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-rpccookiefile=<loc>", _("Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)"), false, OptionsCategory::OPTIONS); gArgs.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", false, OptionsCategory::OPTIONS); gArgs.AddArg("-rpcport=<port>", strprintf("Connect to JSON-RPC on <port> (default: %u or testnet: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort()), false, OptionsCategory::OPTIONS); gArgs.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", false, OptionsCategory::OPTIONS); @@ -49,6 +50,10 @@ static void SetupCliArgs() gArgs.AddArg("-rpcwallet=<walletname>", "Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind)", false, OptionsCategory::OPTIONS); gArgs.AddArg("-stdin", "Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.", false, OptionsCategory::OPTIONS); gArgs.AddArg("-stdinrpcpass", strprintf("Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password."), false, OptionsCategory::OPTIONS); + + // Hidden + gArgs.AddArg("-h", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-help", "", false, OptionsCategory::HIDDEN); } ////////////////////////////////////////////////////////////////////////////// @@ -80,7 +85,11 @@ static int AppInitRPC(int argc, char* argv[]) // Parameters // SetupCliArgs(); - gArgs.ParseParameters(argc, argv); + std::string error; + if (!gArgs.ParseParameters(argc, argv, error)) { + fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str()); + return EXIT_FAILURE; + } if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) { std::string strUsage = strprintf("%s RPC client version", PACKAGE_NAME) + " " + FormatFullVersion() + "\n"; if (!gArgs.IsArgSet("-version")) { @@ -104,10 +113,8 @@ static int AppInitRPC(int argc, char* argv[]) fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str()); return EXIT_FAILURE; } - try { - gArgs.ReadConfigFiles(); - } catch (const std::exception& e) { - fprintf(stderr,"Error reading configuration file: %s\n", e.what()); + if (!gArgs.ReadConfigFiles(error, true)) { + fprintf(stderr, "Error reading configuration file: %s\n", error.c_str()); return EXIT_FAILURE; } // Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause) diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 8b542dc9b8..3fb505d739 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -64,6 +64,10 @@ static void SetupBitcoinTxArgs() gArgs.AddArg("load=NAME:FILENAME", "Load JSON file FILENAME into register NAME", false, OptionsCategory::REGISTER_COMMANDS); gArgs.AddArg("set=NAME:JSON-STRING", "Set register NAME to given JSON-STRING", false, OptionsCategory::REGISTER_COMMANDS); + + // Hidden + gArgs.AddArg("-h", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-help", "", false, OptionsCategory::HIDDEN); } // @@ -76,7 +80,11 @@ static int AppInitRawTx(int argc, char* argv[]) // Parameters // SetupBitcoinTxArgs(); - gArgs.ParseParameters(argc, argv); + std::string error; + if (!gArgs.ParseParameters(argc, argv, error)) { + fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str()); + return EXIT_FAILURE; + } // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause) try { diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 35c05dad41..a9b952e5a4 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -65,7 +65,11 @@ static bool AppInit(int argc, char* argv[]) #if HAVE_DECL_DAEMON gArgs.AddArg("-daemon", "Run in the background as a daemon and accept commands", false, OptionsCategory::OPTIONS); #endif - gArgs.ParseParameters(argc, argv); + std::string error; + if (!gArgs.ParseParameters(argc, argv, error)) { + fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str()); + return false; + } // Process help and version before taking care about datadir if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) { @@ -94,11 +98,8 @@ static bool AppInit(int argc, char* argv[]) fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str()); return false; } - try - { - gArgs.ReadConfigFiles(); - } catch (const std::exception& e) { - fprintf(stderr,"Error reading configuration file: %s\n", e.what()); + if (!gArgs.ReadConfigFiles(error)) { + fprintf(stderr, "Error reading configuration file: %s\n", error.c_str()); return false; } // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause) diff --git a/src/init.cpp b/src/init.cpp index 882371946c..b4e2eec0d2 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -499,6 +499,23 @@ void SetupServerArgs() gArgs.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", false, OptionsCategory::RPC); gArgs.AddArg("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE), true, OptionsCategory::RPC); gArgs.AddArg("-server", "Accept command line and JSON-RPC commands", false, OptionsCategory::RPC); + + // Hidden options + gArgs.AddArg("-rpcssl", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-benchmark", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-h", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-help", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-socks", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-tor", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-debugnet", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-whitelistalwaysrelay", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-prematurewitness", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-walletprematurewitness", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-promiscuousmempoolflags", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-blockminsize", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-dbcrashratio", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-forcecompactdb", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-usehd", "", false, OptionsCategory::HIDDEN); } std::string LicenseInfo() diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index 9270351176..4189ff7497 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -48,11 +48,11 @@ namespace { class NodeImpl : public Node { - void parseParameters(int argc, const char* const argv[]) override + bool parseParameters(int argc, const char* const argv[], std::string& error) override { - gArgs.ParseParameters(argc, argv); + return gArgs.ParseParameters(argc, argv, error); } - void readConfigFiles() override { gArgs.ReadConfigFiles(); } + bool readConfigFiles(std::string& error) override { return gArgs.ReadConfigFiles(error); } bool softSetArg(const std::string& arg, const std::string& value) override { return gArgs.SoftSetArg(arg, value); } bool softSetBoolArg(const std::string& arg, bool value) override { return gArgs.SoftSetBoolArg(arg, value); } void selectParams(const std::string& network) override { SelectParams(network); } diff --git a/src/interfaces/node.h b/src/interfaces/node.h index 2a1a8152df..8185c015a9 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -38,7 +38,7 @@ public: virtual ~Node() {} //! Set command line arguments. - virtual void parseParameters(int argc, const char* const argv[]) = 0; + virtual bool parseParameters(int argc, const char* const argv[], std::string& error) = 0; //! Set a command line argument if it doesn't already have a value virtual bool softSetArg(const std::string& arg, const std::string& value) = 0; @@ -47,7 +47,7 @@ public: virtual bool softSetBoolArg(const std::string& arg, bool value) = 0; //! Load settings from configuration file. - virtual void readConfigFiles() = 0; + virtual bool readConfigFiles(std::string& error) = 0; //! Choose network parameters. virtual void selectParams(const std::string& network) = 0; diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 87dfdb73d3..31d9f936e7 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -229,6 +229,9 @@ public: /// Get window identifier of QMainWindow (BitcoinGUI) WId getMainWinId() const; + /// Setup platform style + void setupPlatformStyle(); + public Q_SLOTS: void initializeResult(bool success); void shutdownResult(); @@ -315,10 +318,14 @@ BitcoinApplication::BitcoinApplication(interfaces::Node& node, int &argc, char * paymentServer(0), m_wallet_models(), #endif - returnValue(0) + returnValue(0), + platformStyle(0) { setQuitOnLastWindowClosed(false); +} +void BitcoinApplication::setupPlatformStyle() +{ // UI per-platform customization // This must be done inside the BitcoinApplication constructor, or after it, because // PlatformStyle::instantiate requires a QApplication @@ -562,15 +569,9 @@ int main(int argc, char *argv[]) std::unique_ptr<interfaces::Node> node = interfaces::MakeNode(); - /// 1. Parse command-line options. These take precedence over anything else. - // Command-line options take precedence: - node->setupServerArgs(); - SetupUIArgs(); - node->parseParameters(argc, argv); - // Do not refer to data directory yet, this can be overridden by Intro::pickDataDirectory - /// 2. Basic Qt initialization (not dependent on parameters or configuration) + /// 1. Basic Qt initialization (not dependent on parameters or configuration) #if QT_VERSION < 0x050000 // Internal string conversion is all UTF-8 QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); @@ -609,6 +610,20 @@ int main(int argc, char *argv[]) qRegisterMetaType<WalletModel*>("WalletModel*"); #endif + /// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these + // Command-line options take precedence: + node->setupServerArgs(); + SetupUIArgs(); + std::string error; + if (!node->parseParameters(argc, argv, error)) { + QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), + QObject::tr("Error parsing command line arguments: %1.").arg(QString::fromStdString(error))); + return EXIT_FAILURE; + } + + // Now that the QApplication is setup and we have parsed our parameters, we can set the platform style + app.setupPlatformStyle(); + /// 3. Application identification // must be set before OptionsModel is initialized or translations are loaded, // as it is used to locate QSettings @@ -644,11 +659,9 @@ int main(int argc, char *argv[]) QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(gArgs.GetArg("-datadir", "")))); return EXIT_FAILURE; } - try { - node->readConfigFiles(); - } catch (const std::exception& e) { + if (!node->readConfigFiles(error)) { QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), - QObject::tr("Error: Cannot parse configuration file: %1. Only use key=value syntax.").arg(e.what())); + QObject::tr("Error: Cannot parse configuration file: %1.").arg(QString::fromStdString(error))); return EXIT_FAILURE; } diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index cd603b7f58..c065e25676 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -27,11 +27,21 @@ static void ResetArgs(const std::string& strArg) for (std::string& s : vecArg) vecChar.push_back(s.c_str()); - gArgs.ParseParameters(vecChar.size(), vecChar.data()); + std::string error; + gArgs.ParseParameters(vecChar.size(), vecChar.data(), error); +} + +static void SetupArgs(const std::vector<std::string>& args) +{ + gArgs.ClearArgs(); + for (const std::string& arg : args) { + gArgs.AddArg(arg, "", false, OptionsCategory::OPTIONS); + } } BOOST_AUTO_TEST_CASE(boolarg) { + SetupArgs({"-foo"}); ResetArgs("-foo"); BOOST_CHECK(gArgs.GetBoolArg("-foo", false)); BOOST_CHECK(gArgs.GetBoolArg("-foo", true)); @@ -84,6 +94,7 @@ BOOST_AUTO_TEST_CASE(boolarg) BOOST_AUTO_TEST_CASE(stringarg) { + SetupArgs({"-foo", "-bar"}); ResetArgs(""); BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), ""); BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "eleven"); @@ -108,6 +119,7 @@ BOOST_AUTO_TEST_CASE(stringarg) BOOST_AUTO_TEST_CASE(intarg) { + SetupArgs({"-foo", "-bar"}); ResetArgs(""); BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 11), 11); BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 0), 0); @@ -127,6 +139,7 @@ BOOST_AUTO_TEST_CASE(intarg) BOOST_AUTO_TEST_CASE(doubledash) { + SetupArgs({"-foo", "-bar"}); ResetArgs("--foo"); BOOST_CHECK_EQUAL(gArgs.GetBoolArg("-foo", false), true); @@ -137,6 +150,7 @@ BOOST_AUTO_TEST_CASE(doubledash) BOOST_AUTO_TEST_CASE(boolargno) { + SetupArgs({"-foo", "-bar"}); ResetArgs("-nofoo"); BOOST_CHECK(!gArgs.GetBoolArg("-foo", true)); BOOST_CHECK(!gArgs.GetBoolArg("-foo", false)); diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 2af6d9e0f3..611ccc9b77 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -186,27 +186,37 @@ struct TestArgsManager : public ArgsManager LOCK(cs_args); m_config_args.clear(); } - ReadConfigStream(streamConfig); + std::string error; + ReadConfigStream(streamConfig, error); } void SetNetworkOnlyArg(const std::string arg) { LOCK(cs_args); m_network_only_args.insert(arg); } + void SetupArgs(int argv, const char* args[]) + { + for (int i = 0; i < argv; ++i) { + AddArg(args[i], "", false, OptionsCategory::OPTIONS); + } + } }; BOOST_AUTO_TEST_CASE(util_ParseParameters) { TestArgsManager testArgs; + const char* avail_args[] = {"-a", "-b", "-ccc", "-d"}; const char *argv_test[] = {"-ignored", "-a", "-b", "-ccc=argument", "-ccc=multiple", "f", "-d=e"}; - testArgs.ParseParameters(0, (char**)argv_test); + std::string error; + testArgs.SetupArgs(4, avail_args); + testArgs.ParseParameters(0, (char**)argv_test, error); BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty()); - testArgs.ParseParameters(1, (char**)argv_test); + testArgs.ParseParameters(1, (char**)argv_test, error); BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty()); - testArgs.ParseParameters(7, (char**)argv_test); + testArgs.ParseParameters(7, (char**)argv_test, error); // expectation: -ignored is ignored (program name argument), // -a, -b and -ccc end up in map, -d ignored because it is after // a non-option argument (non-GNU option parsing) @@ -227,9 +237,12 @@ BOOST_AUTO_TEST_CASE(util_ParseParameters) BOOST_AUTO_TEST_CASE(util_GetBoolArg) { TestArgsManager testArgs; + const char* avail_args[] = {"-a", "-b", "-c", "-d", "-e", "-f"}; const char *argv_test[] = { "ignored", "-a", "-nob", "-c=0", "-d=1", "-e=false", "-f=true"}; - testArgs.ParseParameters(7, (char**)argv_test); + std::string error; + testArgs.SetupArgs(6, avail_args); + testArgs.ParseParameters(7, (char**)argv_test, error); // Each letter should be set. for (char opt : "abcdef") @@ -261,8 +274,11 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases) TestArgsManager testArgs; // Params test + const char* avail_args[] = {"-foo", "-bar"}; const char *argv_test[] = {"ignored", "-nofoo", "-foo", "-nobar=0"}; - testArgs.ParseParameters(4, (char**)argv_test); + testArgs.SetupArgs(2, avail_args); + std::string error; + testArgs.ParseParameters(4, (char**)argv_test, error); // This was passed twice, second one overrides the negative setting. BOOST_CHECK(!testArgs.IsArgNegated("-foo")); @@ -274,7 +290,7 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases) // Config test const char *conf_test = "nofoo=1\nfoo=1\nnobar=0\n"; - testArgs.ParseParameters(1, (char**)argv_test); + testArgs.ParseParameters(1, (char**)argv_test, error); testArgs.ReadConfigString(conf_test); // This was passed twice, second one overrides the negative setting, @@ -289,7 +305,7 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases) // Combined test const char *combo_test_args[] = {"ignored", "-nofoo", "-bar"}; const char *combo_test_conf = "foo=1\nnobar=1\n"; - testArgs.ParseParameters(3, (char**)combo_test_args); + testArgs.ParseParameters(3, (char**)combo_test_args, error); testArgs.ReadConfigString(combo_test_conf); // Command line overrides, but doesn't erase old setting @@ -329,6 +345,8 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream) "iii=2\n"; TestArgsManager test_args; + const char* avail_args[] = {"-a", "-b", "-ccc", "-d", "-e", "-fff", "-ggg", "-h", "-i", "-iii"}; + test_args.SetupArgs(10, avail_args); test_args.ReadConfigString(str_config); // expectation: a, b, ccc, d, fff, ggg, h, i end up in map @@ -526,6 +544,8 @@ BOOST_AUTO_TEST_CASE(util_GetArg) BOOST_AUTO_TEST_CASE(util_GetChainName) { TestArgsManager test_args; + const char* avail_args[] = {"-testnet", "-regtest"}; + test_args.SetupArgs(2, avail_args); const char* argv_testnet[] = {"cmd", "-testnet"}; const char* argv_regtest[] = {"cmd", "-regtest"}; @@ -535,39 +555,40 @@ BOOST_AUTO_TEST_CASE(util_GetChainName) // equivalent to "-testnet" // regtest in testnet section is ignored const char* testnetconf = "testnet=1\nregtest=0\n[test]\nregtest=1"; + std::string error; - test_args.ParseParameters(0, (char**)argv_testnet); + test_args.ParseParameters(0, (char**)argv_testnet, error); BOOST_CHECK_EQUAL(test_args.GetChainName(), "main"); - test_args.ParseParameters(2, (char**)argv_testnet); + test_args.ParseParameters(2, (char**)argv_testnet, error); BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - test_args.ParseParameters(2, (char**)argv_regtest); + test_args.ParseParameters(2, (char**)argv_regtest, error); BOOST_CHECK_EQUAL(test_args.GetChainName(), "regtest"); - test_args.ParseParameters(3, (char**)argv_test_no_reg); + test_args.ParseParameters(3, (char**)argv_test_no_reg, error); BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - test_args.ParseParameters(3, (char**)argv_both); + test_args.ParseParameters(3, (char**)argv_both, error); BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); - test_args.ParseParameters(0, (char**)argv_testnet); + test_args.ParseParameters(0, (char**)argv_testnet, error); test_args.ReadConfigString(testnetconf); BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - test_args.ParseParameters(2, (char**)argv_testnet); + test_args.ParseParameters(2, (char**)argv_testnet, error); test_args.ReadConfigString(testnetconf); BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - test_args.ParseParameters(2, (char**)argv_regtest); + test_args.ParseParameters(2, (char**)argv_regtest, error); test_args.ReadConfigString(testnetconf); BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); - test_args.ParseParameters(3, (char**)argv_test_no_reg); + test_args.ParseParameters(3, (char**)argv_test_no_reg, error); test_args.ReadConfigString(testnetconf); BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - test_args.ParseParameters(3, (char**)argv_both); + test_args.ParseParameters(3, (char**)argv_both, error); test_args.ReadConfigString(testnetconf); BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); @@ -575,23 +596,23 @@ BOOST_AUTO_TEST_CASE(util_GetChainName) // [test] regtest=1 potentially relevant) doesn't break things test_args.SelectConfigNetwork("test"); - test_args.ParseParameters(0, (char**)argv_testnet); + test_args.ParseParameters(0, (char**)argv_testnet, error); test_args.ReadConfigString(testnetconf); BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - test_args.ParseParameters(2, (char**)argv_testnet); + test_args.ParseParameters(2, (char**)argv_testnet, error); test_args.ReadConfigString(testnetconf); BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - test_args.ParseParameters(2, (char**)argv_regtest); + test_args.ParseParameters(2, (char**)argv_regtest, error); test_args.ReadConfigString(testnetconf); BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); - test_args.ParseParameters(2, (char**)argv_test_no_reg); + test_args.ParseParameters(2, (char**)argv_test_no_reg, error); test_args.ReadConfigString(testnetconf); BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - test_args.ParseParameters(3, (char**)argv_both); + test_args.ParseParameters(3, (char**)argv_both, error); test_args.ReadConfigString(testnetconf); BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); } diff --git a/src/util.cpp b/src/util.cpp index 963a7f531f..34483d95b0 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -412,7 +412,7 @@ void ArgsManager::SelectConfigNetwork(const std::string& network) m_network = network; } -void ArgsManager::ParseParameters(int argc, const char* const argv[]) +bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::string& error) { LOCK(cs_args); m_override_args.clear(); @@ -444,6 +444,14 @@ void ArgsManager::ParseParameters(int argc, const char* const argv[]) } else { m_override_args[key].push_back(val); } + + // Check that the arg is known + if (!(IsSwitchChar(key[0]) && key.size() == 1)) { + if (!IsArgKnown(key, error)) { + error = strprintf("Invalid parameter %s", key.c_str()); + return false; + } + } } // we do not allow -includeconf from command line, so we clear it here @@ -456,6 +464,23 @@ void ArgsManager::ParseParameters(int argc, const char* const argv[]) m_override_args.erase(it); } } + return true; +} + +bool ArgsManager::IsArgKnown(const std::string& key, std::string& error) +{ + size_t option_index = key.find('.'); + std::string arg_no_net; + if (option_index == std::string::npos) { + arg_no_net = key; + } else { + arg_no_net = std::string("-") + key.substr(option_index + 1, std::string::npos); + } + + for (const auto& arg_map : m_available_args) { + if (arg_map.second.count(arg_no_net)) return true; + } + return false; } std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const @@ -549,48 +574,80 @@ void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strV void ArgsManager::AddArg(const std::string& name, const std::string& help, const bool debug_only, const OptionsCategory& cat) { - std::pair<OptionsCategory, std::string> key(cat, name); - assert(m_available_args.count(key) == 0); - m_available_args.emplace(key, std::pair<std::string, bool>(help, debug_only)); + // Split arg name from its help param + size_t eq_index = name.find('='); + if (eq_index == std::string::npos) { + eq_index = name.size(); + } + + std::map<std::string, Arg>& arg_map = m_available_args[cat]; + auto ret = arg_map.emplace(name.substr(0, eq_index), Arg(name.substr(eq_index, name.size() - eq_index), help, debug_only)); + assert(ret.second); // Make sure an insertion actually happened } std::string ArgsManager::GetHelpMessage() { const bool show_debug = gArgs.GetBoolArg("-help-debug", false); - std::string usage = HelpMessageGroup("Options:"); - - OptionsCategory last_cat = OptionsCategory::OPTIONS; - for (auto& arg : m_available_args) { - if (arg.first.first != last_cat) { - last_cat = arg.first.first; - if (last_cat == OptionsCategory::CONNECTION) + std::string usage = ""; + for (const auto& arg_map : m_available_args) { + switch(arg_map.first) { + case OptionsCategory::OPTIONS: + usage += HelpMessageGroup("Options:"); + break; + case OptionsCategory::CONNECTION: usage += HelpMessageGroup("Connection options:"); - else if (last_cat == OptionsCategory::ZMQ) + break; + case OptionsCategory::ZMQ: usage += HelpMessageGroup("ZeroMQ notification options:"); - else if (last_cat == OptionsCategory::DEBUG_TEST) + break; + case OptionsCategory::DEBUG_TEST: usage += HelpMessageGroup("Debugging/Testing options:"); - else if (last_cat == OptionsCategory::NODE_RELAY) + break; + case OptionsCategory::NODE_RELAY: usage += HelpMessageGroup("Node relay options:"); - else if (last_cat == OptionsCategory::BLOCK_CREATION) + break; + case OptionsCategory::BLOCK_CREATION: usage += HelpMessageGroup("Block creation options:"); - else if (last_cat == OptionsCategory::RPC) + break; + case OptionsCategory::RPC: usage += HelpMessageGroup("RPC server options:"); - else if (last_cat == OptionsCategory::WALLET) + break; + case OptionsCategory::WALLET: usage += HelpMessageGroup("Wallet options:"); - else if (last_cat == OptionsCategory::WALLET_DEBUG_TEST && show_debug) - usage += HelpMessageGroup("Wallet debugging/testing options:"); - else if (last_cat == OptionsCategory::CHAINPARAMS) + break; + case OptionsCategory::WALLET_DEBUG_TEST: + if (show_debug) usage += HelpMessageGroup("Wallet debugging/testing options:"); + break; + case OptionsCategory::CHAINPARAMS: usage += HelpMessageGroup("Chain selection options:"); - else if (last_cat == OptionsCategory::GUI) + break; + case OptionsCategory::GUI: usage += HelpMessageGroup("UI Options:"); - else if (last_cat == OptionsCategory::COMMANDS) + break; + case OptionsCategory::COMMANDS: usage += HelpMessageGroup("Commands:"); - else if (last_cat == OptionsCategory::REGISTER_COMMANDS) + break; + case OptionsCategory::REGISTER_COMMANDS: usage += HelpMessageGroup("Register Commands:"); + break; + default: + break; } - if (show_debug || !arg.second.second) { - usage += HelpMessageOpt(arg.first.second, arg.second.first); + + // When we get to the hidden options, stop + if (arg_map.first == OptionsCategory::HIDDEN) break; + + for (const auto& arg : arg_map.second) { + if (show_debug || !arg.second.m_debug_only) { + std::string name; + if (arg.second.m_help_param.empty()) { + name = arg.first; + } else { + name = arg.first + arg.second.m_help_param; + } + usage += HelpMessageOpt(name, arg.second.m_help_text); + } } } return usage; @@ -747,7 +804,7 @@ fs::path GetConfigFile(const std::string& confPath) return AbsPathForConfigVal(fs::path(confPath), false); } -void ArgsManager::ReadConfigStream(std::istream& stream) +bool ArgsManager::ReadConfigStream(std::istream& stream, std::string& error, bool ignore_invalid_keys) { LOCK(cs_args); @@ -758,15 +815,23 @@ void ArgsManager::ReadConfigStream(std::istream& stream) { std::string strKey = std::string("-") + it->string_key; std::string strValue = it->value[0]; + if (InterpretNegatedOption(strKey, strValue)) { m_config_args[strKey].clear(); } else { m_config_args[strKey].push_back(strValue); } + + // Check that the arg is known + if (!IsArgKnown(strKey, error) && !ignore_invalid_keys) { + error = strprintf("Invalid configuration value %s", it->string_key.c_str()); + return false; + } } + return true; } -void ArgsManager::ReadConfigFiles() +bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys) { { LOCK(cs_args); @@ -778,7 +843,9 @@ void ArgsManager::ReadConfigFiles() // ok to not have a config file if (stream.good()) { - ReadConfigStream(stream); + if (!ReadConfigStream(stream, error, ignore_invalid_keys)) { + return false; + } // if there is an -includeconf in the override args, but it is empty, that means the user // passed '-noincludeconf' on the command line, in which case we should not include anything if (m_override_args.count("-includeconf") == 0) { @@ -801,7 +868,9 @@ void ArgsManager::ReadConfigFiles() for (const std::string& to_include : includeconf) { fs::ifstream include_config(GetConfigFile(to_include)); if (include_config.good()) { - ReadConfigStream(include_config); + if (!ReadConfigStream(include_config, error, ignore_invalid_keys)) { + return false; + } LogPrintf("Included configuration file %s\n", to_include.c_str()); } else { fprintf(stderr, "Failed to include configuration file %s\n", to_include.c_str()); @@ -823,8 +892,10 @@ void ArgsManager::ReadConfigFiles() // If datadir is changed in .conf file: ClearDatadirCache(); if (!fs::is_directory(GetDataDir(false))) { - throw std::runtime_error(strprintf("specified data directory \"%s\" does not exist.", gArgs.GetArg("-datadir", "").c_str())); + error = strprintf("specified data directory \"%s\" does not exist.", gArgs.GetArg("-datadir", "").c_str()); + return false; } + return true; } std::string ArgsManager::GetChainName() const diff --git a/src/util.h b/src/util.h index 17dd5c0683..62c3f7c18b 100644 --- a/src/util.h +++ b/src/util.h @@ -118,8 +118,7 @@ inline bool IsSwitchChar(char c) #endif } -enum class OptionsCategory -{ +enum class OptionsCategory { OPTIONS, CONNECTION, WALLET, @@ -132,7 +131,9 @@ enum class OptionsCategory RPC, GUI, COMMANDS, - REGISTER_COMMANDS + REGISTER_COMMANDS, + + HIDDEN // Always the last option to avoid printing these in the help }; class ArgsManager @@ -140,14 +141,23 @@ class ArgsManager protected: friend class ArgsManagerHelper; + struct Arg + { + std::string m_help_param; + std::string m_help_text; + bool m_debug_only; + + Arg(const std::string& help_param, const std::string& help_text, bool debug_only) : m_help_param(help_param), m_help_text(help_text), m_debug_only(debug_only) {}; + }; + mutable CCriticalSection cs_args; std::map<std::string, std::vector<std::string>> m_override_args; std::map<std::string, std::vector<std::string>> m_config_args; std::string m_network; std::set<std::string> m_network_only_args; - std::map<std::pair<OptionsCategory, std::string>, std::pair<std::string, bool>> m_available_args; + std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args; - void ReadConfigStream(std::istream& stream); + bool ReadConfigStream(std::istream& stream, std::string& error, bool ignore_invalid_keys = false); public: ArgsManager(); @@ -157,8 +167,8 @@ public: */ void SelectConfigNetwork(const std::string& network); - void ParseParameters(int argc, const char*const argv[]); - void ReadConfigFiles(); + bool ParseParameters(int argc, const char* const argv[], std::string& error); + bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false); /** * Log warnings for options in m_section_only_args when @@ -254,9 +264,19 @@ public: void AddArg(const std::string& name, const std::string& help, const bool debug_only, const OptionsCategory& cat); /** + * Clear available arguments + */ + void ClearArgs() { m_available_args.clear(); } + + /** * Get the help string */ std::string GetHelpMessage(); + + /** + * Check whether we know of this arg + */ + bool IsArgKnown(const std::string& key, std::string& error); }; extern ArgsManager gArgs; |