Give an error and exit if there are unknown parameters
If an unknown option is given via either the command line args or the conf file, throw an error and exit Update tests for ArgsManager knowing args Ignore unknown options in the config file for bitcoin-cli Fix tests and bitcoin-cli to match actual options used
This commit is contained in:
parent
174f7c8080
commit
4f8704d57f
12 changed files with 205 additions and 66 deletions
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
17
src/init.cpp
17
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()
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
51
src/util.cpp
51
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
|
||||
|
@ -779,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);
|
||||
|
||||
|
@ -790,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);
|
||||
|
@ -810,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) {
|
||||
|
@ -833,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());
|
||||
|
@ -855,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
|
||||
|
|
23
src/util.h
23
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
|
||||
|
@ -156,7 +157,7 @@ protected:
|
|||
std::set<std::string> m_network_only_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();
|
||||
|
@ -166,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
|
||||
|
@ -262,10 +263,20 @@ 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;
|
||||
|
|
Loading…
Reference in a new issue