Line data Source code
1 : // Copyright (c) 2009-2010 Satoshi Nakamoto
2 : // Copyright (c) 2009-2022 The Bitcoin Core developers
3 : // Distributed under the MIT software license, see the accompanying
4 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 :
6 : #include <common/args.h>
7 :
8 : #include <chainparamsbase.h>
9 : #include <common/settings.h>
10 : #include <logging.h>
11 : #include <sync.h>
12 : #include <tinyformat.h>
13 : #include <univalue.h>
14 : #include <util/chaintype.h>
15 : #include <util/check.h>
16 : #include <util/fs.h>
17 : #include <util/fs_helpers.h>
18 : #include <util/strencodings.h>
19 :
20 : #ifdef WIN32
21 : #include <codecvt> /* for codecvt_utf8_utf16 */
22 : #include <shellapi.h> /* for CommandLineToArgvW */
23 : #include <shlobj.h> /* for CSIDL_APPDATA */
24 : #endif
25 :
26 : #include <algorithm>
27 : #include <cassert>
28 : #include <cstdint>
29 : #include <cstdlib>
30 : #include <cstring>
31 : #include <filesystem>
32 : #include <map>
33 : #include <optional>
34 : #include <stdexcept>
35 : #include <string>
36 : #include <utility>
37 : #include <variant>
38 :
39 : const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
40 : const char * const BITCOIN_SETTINGS_FILENAME = "settings.json";
41 :
42 173 : ArgsManager gArgs;
43 :
44 : /**
45 : * Interpret a string argument as a boolean.
46 : *
47 : * The definition of LocaleIndependentAtoi<int>() requires that non-numeric string values
48 : * like "foo", return 0. This means that if a user unintentionally supplies a
49 : * non-integer argument here, the return value is always false. This means that
50 : * -foo=false does what the user probably expects, but -foo=true is well defined
51 : * but does not do what they probably expected.
52 : *
53 : * The return value of LocaleIndependentAtoi<int>(...) is zero when given input not
54 : * representable as an int.
55 : *
56 : * For a more extensive discussion of this topic (and a wide range of opinions
57 : * on the Right Way to change this code), see PR12713.
58 : */
59 3117 : static bool InterpretBool(const std::string& strValue)
60 : {
61 3117 : if (strValue.empty())
62 209 : return true;
63 2908 : return (LocaleIndependentAtoi<int>(strValue) != 0);
64 3117 : }
65 :
66 176916 : static std::string SettingName(const std::string& arg)
67 : {
68 176916 : return arg.size() > 0 && arg[0] == '-' ? arg.substr(1) : arg;
69 : }
70 :
71 : /**
72 : * Parse "name", "section.name", "noname", "section.noname" settings keys.
73 : *
74 173 : * @note Where an option was negated can be later checked using the
75 : * IsArgNegated() method. One use case for this is to have a way to disable
76 : * options that are not normally boolean (e.g. using -nodebuglogfile to request
77 : * that debug log output is not sent to any file at all).
78 : */
79 692 : KeyInfo InterpretKey(std::string key)
80 : {
81 692 : KeyInfo result;
82 : // Split section name from key name for keys like "testnet.foo" or "regtest.bar"
83 692 : size_t option_index = key.find('.');
84 692 : if (option_index != std::string::npos) {
85 0 : result.section = key.substr(0, option_index);
86 0 : key.erase(0, option_index + 1);
87 0 : }
88 692 : if (key.substr(0, 2) == "no") {
89 138 : key.erase(0, 2);
90 138 : result.negated = true;
91 138 : }
92 692 : result.name = key;
93 692 : return result;
94 692 : }
95 :
96 : /**
97 : * Interpret settings value based on registered flags.
98 : *
99 : * @param[in] key key information to know if key was negated
100 : * @param[in] value string value of setting to be parsed
101 : * @param[in] flags ArgsManager registered argument flags
102 : * @param[out] error Error description if settings value is not valid
103 : *
104 : * @return parsed settings value if it is valid, otherwise nullopt accompanied
105 : * by a descriptive error string
106 : */
107 692 : std::optional<common::SettingsValue> InterpretValue(const KeyInfo& key, const std::string* value,
108 : unsigned int flags, std::string& error)
109 : {
110 : // Return negated settings as false values.
111 692 : if (key.negated) {
112 138 : if (flags & ArgsManager::DISALLOW_NEGATION) {
113 0 : error = strprintf("Negating of -%s is meaningless and therefore forbidden", key.name);
114 0 : return std::nullopt;
115 : }
116 : // Double negatives like -nofoo=0 are supported (but discouraged)
117 138 : if (value && !InterpretBool(*value)) {
118 0 : LogPrintf("Warning: parsed potentially confusing double-negative -%s=%s\n", key.name, *value);
119 0 : return true;
120 : }
121 138 : return false;
122 : }
123 554 : if (!value && (flags & ArgsManager::DISALLOW_ELISION)) {
124 0 : error = strprintf("Can not set -%s with no value. Please specify value with -%s=value.", key.name, key.name);
125 0 : return std::nullopt;
126 : }
127 554 : return value ? *value : "";
128 692 : }
129 :
130 : // Define default constructor and destructor that are not inline, so code instantiating this class doesn't need to
131 : // #include class definitions for all members.
132 : // For example, m_settings has an internal dependency on univalue.
133 488 : ArgsManager::ArgsManager() = default;
134 244 : ArgsManager::~ArgsManager() = default;
135 :
136 69 : std::set<std::string> ArgsManager::GetUnsuitableSectionOnlyArgs() const
137 : {
138 313 : std::set<std::string> unsuitables;
139 :
140 69 : LOCK(cs_args);
141 :
142 : // if there's no section selected, don't worry
143 69 : if (m_network.empty()) return std::set<std::string> {};
144 :
145 : // if it's okay to use the default section for this network, don't worry
146 69 : if (m_network == ChainTypeToString(ChainType::MAIN)) return std::set<std::string> {};
147 :
148 603 : for (const auto& arg : m_network_only_args) {
149 536 : if (OnlyHasDefaultSectionSetting(m_settings, m_network, SettingName(arg))) {
150 0 : unsuitables.insert(arg);
151 0 : }
152 : }
153 67 : return unsuitables;
154 69 : }
155 :
156 69 : std::list<SectionInfo> ArgsManager::GetUnrecognizedSections() const
157 : {
158 : // Section names to be recognized in the config file.
159 69 : static const std::set<std::string> available_sections{
160 69 : ChainTypeToString(ChainType::REGTEST),
161 69 : ChainTypeToString(ChainType::SIGNET),
162 69 : ChainTypeToString(ChainType::TESTNET),
163 69 : ChainTypeToString(ChainType::MAIN),
164 : };
165 :
166 69 : LOCK(cs_args);
167 69 : std::list<SectionInfo> unrecognized = m_config_sections;
168 69 : unrecognized.remove_if([](const SectionInfo& appeared){ return available_sections.find(appeared.m_name) != available_sections.end(); });
169 69 : return unrecognized;
170 69 : }
171 :
172 90 : void ArgsManager::SelectConfigNetwork(const std::string& network)
173 : {
174 90 : LOCK(cs_args);
175 90 : m_network = network;
176 90 : }
177 :
178 69 : bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::string& error)
179 : {
180 69 : LOCK(cs_args);
181 69 : m_settings.command_line_options.clear();
182 :
183 761 : for (int i = 1; i < argc; i++) {
184 692 : std::string key(argv[i]);
185 :
186 : #ifdef MAC_OSX
187 : // At the first time when a user gets the "App downloaded from the
188 : // internet" warning, and clicks the Open button, macOS passes
189 : // a unique process serial number (PSN) as -psn_... command-line
190 : // argument, which we filter out.
191 : if (key.substr(0, 5) == "-psn_") continue;
192 : #endif
193 :
194 692 : if (key == "-") break; //bitcoin-tx using stdin
195 692 : std::optional<std::string> val;
196 692 : size_t is_index = key.find('=');
197 692 : if (is_index != std::string::npos) {
198 276 : val = key.substr(is_index + 1);
199 276 : key.erase(is_index);
200 276 : }
201 : #ifdef WIN32
202 : key = ToLower(key);
203 : if (key[0] == '/')
204 : key[0] = '-';
205 : #endif
206 :
207 692 : if (key[0] != '-') {
208 0 : if (!m_accept_any_command && m_command.empty()) {
209 : // The first non-dash arg is a registered command
210 0 : std::optional<unsigned int> flags = GetArgFlags(key);
211 0 : if (!flags || !(*flags & ArgsManager::COMMAND)) {
212 0 : error = strprintf("Invalid command '%s'", argv[i]);
213 0 : return false;
214 : }
215 0 : }
216 0 : m_command.push_back(key);
217 0 : while (++i < argc) {
218 : // The remaining args are command args
219 0 : m_command.push_back(argv[i]);
220 : }
221 0 : break;
222 : }
223 :
224 : // Transform --foo to -foo
225 692 : if (key.length() > 1 && key[1] == '-')
226 0 : key.erase(0, 1);
227 :
228 : // Transform -foo to foo
229 692 : key.erase(0, 1);
230 692 : KeyInfo keyinfo = InterpretKey(key);
231 692 : std::optional<unsigned int> flags = GetArgFlags('-' + keyinfo.name);
232 :
233 : // Unknown command line options and command line options with dot
234 : // characters (which are returned from InterpretKey with nonempty
235 : // section strings) are not valid.
236 692 : if (!flags || !keyinfo.section.empty()) {
237 0 : error = strprintf("Invalid parameter %s", argv[i]);
238 0 : return false;
239 : }
240 :
241 692 : std::optional<common::SettingsValue> value = InterpretValue(keyinfo, val ? &*val : nullptr, *flags, error);
242 692 : if (!value) return false;
243 :
244 692 : m_settings.command_line_options[keyinfo.name].push_back(*value);
245 692 : }
246 :
247 : // we do not allow -includeconf from command line, only -noincludeconf
248 69 : if (auto* includes = common::FindKey(m_settings.command_line_options, "includeconf")) {
249 0 : const common::SettingsSpan values{*includes};
250 : // Range may be empty if -noincludeconf was passed
251 0 : if (!values.empty()) {
252 0 : error = "-includeconf cannot be used from commandline; -includeconf=" + values.begin()->write();
253 0 : return false; // pick first value as example
254 : }
255 0 : }
256 69 : return true;
257 69 : }
258 :
259 692 : std::optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) const
260 : {
261 692 : LOCK(cs_args);
262 3178 : for (const auto& arg_map : m_available_args) {
263 3178 : const auto search = arg_map.second.find(name);
264 3178 : if (search != arg_map.second.end()) {
265 692 : return search->second.m_flags;
266 : }
267 : }
268 0 : return std::nullopt;
269 692 : }
270 :
271 239 : fs::path ArgsManager::GetPathArg(std::string arg, const fs::path& default_value) const
272 : {
273 239 : if (IsArgNegated(arg)) return fs::path{};
274 170 : std::string path_str = GetArg(arg, "");
275 170 : if (path_str.empty()) return default_value;
276 170 : fs::path result = fs::PathFromString(path_str).lexically_normal();
277 : // Remove trailing slash, if present.
278 170 : return result.has_filename() ? result : result.parent_path();
279 239 : }
280 :
281 154 : const fs::path& ArgsManager::GetBlocksDirPath() const
282 : {
283 154 : LOCK(cs_args);
284 154 : fs::path& path = m_cached_blocks_path;
285 :
286 : // Cache the path to avoid calling fs::create_directories on every call of
287 : // this function
288 154 : if (!path.empty()) return path;
289 :
290 85 : if (IsArgSet("-blocksdir")) {
291 0 : path = fs::absolute(GetPathArg("-blocksdir"));
292 0 : if (!fs::is_directory(path)) {
293 0 : path = "";
294 0 : return path;
295 : }
296 0 : } else {
297 85 : path = GetDataDirBase();
298 : }
299 :
300 85 : path /= fs::PathFromString(BaseParams().DataDir());
301 85 : path /= "blocks";
302 85 : fs::create_directories(path);
303 85 : return path;
304 154 : }
305 :
306 9109 : const fs::path& ArgsManager::GetDataDir(bool net_specific) const
307 : {
308 9109 : LOCK(cs_args);
309 9109 : fs::path& path = net_specific ? m_cached_network_datadir_path : m_cached_datadir_path;
310 :
311 : // Used cached path if available
312 9109 : if (!path.empty()) return path;
313 :
314 170 : const fs::path datadir{GetPathArg("-datadir")};
315 170 : if (!datadir.empty()) {
316 170 : path = fs::absolute(datadir);
317 170 : if (!fs::is_directory(path)) {
318 0 : path = "";
319 0 : return path;
320 : }
321 170 : } else {
322 0 : path = GetDefaultDataDir();
323 : }
324 :
325 170 : if (net_specific && !BaseParams().DataDir().empty()) {
326 83 : path /= fs::PathFromString(BaseParams().DataDir());
327 83 : }
328 :
329 170 : return path;
330 9109 : }
331 :
332 69 : void ArgsManager::ClearPathCache()
333 : {
334 69 : LOCK(cs_args);
335 :
336 69 : m_cached_datadir_path = fs::path();
337 69 : m_cached_network_datadir_path = fs::path();
338 69 : m_cached_blocks_path = fs::path();
339 69 : }
340 :
341 0 : std::optional<const ArgsManager::Command> ArgsManager::GetCommand() const
342 : {
343 0 : Command ret;
344 0 : LOCK(cs_args);
345 0 : auto it = m_command.begin();
346 0 : if (it == m_command.end()) {
347 : // No command was passed
348 0 : return std::nullopt;
349 : }
350 0 : if (!m_accept_any_command) {
351 : // The registered command
352 0 : ret.command = *(it++);
353 0 : }
354 0 : while (it != m_command.end()) {
355 : // The unregistered command and args (if any)
356 0 : ret.args.push_back(*(it++));
357 : }
358 0 : return ret;
359 0 : }
360 :
361 496 : std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const
362 : {
363 496 : std::vector<std::string> result;
364 703 : for (const common::SettingsValue& value : GetSettingsList(strArg)) {
365 207 : result.push_back(value.isFalse() ? "0" : value.isTrue() ? "1" : value.get_str());
366 : }
367 496 : return result;
368 496 : }
369 :
370 29886 : bool ArgsManager::IsArgSet(const std::string& strArg) const
371 : {
372 29886 : return !GetSetting(strArg).isNull();
373 0 : }
374 :
375 0 : bool ArgsManager::GetSettingsPath(fs::path* filepath, bool temp, bool backup) const
376 : {
377 0 : fs::path settings = GetPathArg("-settings", BITCOIN_SETTINGS_FILENAME);
378 0 : if (settings.empty()) {
379 0 : return false;
380 : }
381 0 : if (backup) {
382 0 : settings += ".bak";
383 0 : }
384 0 : if (filepath) {
385 0 : *filepath = fsbridge::AbsPathJoin(GetDataDirNet(), temp ? settings + ".tmp" : settings);
386 0 : }
387 0 : return true;
388 0 : }
389 :
390 0 : static void SaveErrors(const std::vector<std::string> errors, std::vector<std::string>* error_out)
391 : {
392 0 : for (const auto& error : errors) {
393 0 : if (error_out) {
394 0 : error_out->emplace_back(error);
395 0 : } else {
396 0 : LogPrintf("%s\n", error);
397 : }
398 : }
399 0 : }
400 :
401 0 : bool ArgsManager::ReadSettingsFile(std::vector<std::string>* errors)
402 : {
403 0 : fs::path path;
404 0 : if (!GetSettingsPath(&path, /* temp= */ false)) {
405 0 : return true; // Do nothing if settings file disabled.
406 : }
407 :
408 0 : LOCK(cs_args);
409 0 : m_settings.rw_settings.clear();
410 0 : std::vector<std::string> read_errors;
411 0 : if (!common::ReadSettings(path, m_settings.rw_settings, read_errors)) {
412 0 : SaveErrors(read_errors, errors);
413 0 : return false;
414 : }
415 0 : for (const auto& setting : m_settings.rw_settings) {
416 0 : KeyInfo key = InterpretKey(setting.first); // Split setting key into section and argname
417 0 : if (!GetArgFlags('-' + key.name)) {
418 0 : LogPrintf("Ignoring unknown rw_settings value %s\n", setting.first);
419 0 : }
420 0 : }
421 0 : return true;
422 0 : }
423 :
424 0 : bool ArgsManager::WriteSettingsFile(std::vector<std::string>* errors, bool backup) const
425 : {
426 0 : fs::path path, path_tmp;
427 0 : if (!GetSettingsPath(&path, /*temp=*/false, backup) || !GetSettingsPath(&path_tmp, /*temp=*/true, backup)) {
428 0 : throw std::logic_error("Attempt to write settings file when dynamic settings are disabled.");
429 : }
430 :
431 0 : LOCK(cs_args);
432 0 : std::vector<std::string> write_errors;
433 0 : if (!common::WriteSettings(path_tmp, m_settings.rw_settings, write_errors)) {
434 0 : SaveErrors(write_errors, errors);
435 0 : return false;
436 : }
437 0 : if (!RenameOver(path_tmp, path)) {
438 0 : SaveErrors({strprintf("Failed renaming settings file %s to %s\n", fs::PathToString(path_tmp), fs::PathToString(path))}, errors);
439 0 : return false;
440 : }
441 0 : return true;
442 0 : }
443 :
444 0 : common::SettingsValue ArgsManager::GetPersistentSetting(const std::string& name) const
445 : {
446 0 : LOCK(cs_args);
447 0 : return common::GetSetting(m_settings, m_network, name, !UseDefaultSection("-" + name),
448 : /*ignore_nonpersistent=*/true, /*get_chain_type=*/false);
449 0 : }
450 :
451 308 : bool ArgsManager::IsArgNegated(const std::string& strArg) const
452 : {
453 308 : return GetSetting(strArg).isFalse();
454 0 : }
455 :
456 239 : std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const
457 : {
458 239 : return GetArg(strArg).value_or(strDefault);
459 0 : }
460 :
461 1651 : std::optional<std::string> ArgsManager::GetArg(const std::string& strArg) const
462 : {
463 1651 : const common::SettingsValue value = GetSetting(strArg);
464 1651 : return SettingToString(value);
465 1651 : }
466 :
467 1651 : std::optional<std::string> SettingToString(const common::SettingsValue& value)
468 : {
469 1651 : if (value.isNull()) return std::nullopt;
470 170 : if (value.isFalse()) return "0";
471 170 : if (value.isTrue()) return "1";
472 170 : if (value.isNum()) return value.getValStr();
473 170 : return value.get_str();
474 1651 : }
475 :
476 0 : std::string SettingToString(const common::SettingsValue& value, const std::string& strDefault)
477 : {
478 0 : return SettingToString(value).value_or(strDefault);
479 0 : }
480 :
481 51818 : int64_t ArgsManager::GetIntArg(const std::string& strArg, int64_t nDefault) const
482 : {
483 51818 : return GetIntArg(strArg).value_or(nDefault);
484 : }
485 :
486 91062 : std::optional<int64_t> ArgsManager::GetIntArg(const std::string& strArg) const
487 : {
488 91062 : const common::SettingsValue value = GetSetting(strArg);
489 91062 : return SettingToInt(value);
490 91062 : }
491 :
492 91062 : std::optional<int64_t> SettingToInt(const common::SettingsValue& value)
493 : {
494 91062 : if (value.isNull()) return std::nullopt;
495 0 : if (value.isFalse()) return 0;
496 0 : if (value.isTrue()) return 1;
497 0 : if (value.isNum()) return value.getInt<int64_t>();
498 0 : return LocaleIndependentAtoi<int64_t>(value.get_str());
499 91062 : }
500 :
501 0 : int64_t SettingToInt(const common::SettingsValue& value, int64_t nDefault)
502 : {
503 0 : return SettingToInt(value).value_or(nDefault);
504 : }
505 :
506 49249 : bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const
507 : {
508 49249 : return GetBoolArg(strArg).value_or(fDefault);
509 : }
510 :
511 49793 : std::optional<bool> ArgsManager::GetBoolArg(const std::string& strArg) const
512 : {
513 49793 : const common::SettingsValue value = GetSetting(strArg);
514 49793 : return SettingToBool(value);
515 49793 : }
516 :
517 49793 : std::optional<bool> SettingToBool(const common::SettingsValue& value)
518 : {
519 49793 : if (value.isNull()) return std::nullopt;
520 3117 : if (value.isBool()) return value.get_bool();
521 3117 : return InterpretBool(value.get_str());
522 49793 : }
523 :
524 0 : bool SettingToBool(const common::SettingsValue& value, bool fDefault)
525 : {
526 0 : return SettingToBool(value).value_or(fDefault);
527 : }
528 :
529 0 : bool ArgsManager::SoftSetArg(const std::string& strArg, const std::string& strValue)
530 : {
531 0 : LOCK(cs_args);
532 0 : if (IsArgSet(strArg)) return false;
533 0 : ForceSetArg(strArg, strValue);
534 0 : return true;
535 0 : }
536 :
537 0 : bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue)
538 : {
539 0 : if (fValue)
540 0 : return SoftSetArg(strArg, std::string("1"));
541 : else
542 0 : return SoftSetArg(strArg, std::string("0"));
543 0 : }
544 :
545 2977 : void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue)
546 : {
547 2977 : LOCK(cs_args);
548 2977 : m_settings.forced_settings[SettingName(strArg)] = strValue;
549 2977 : }
550 :
551 0 : void ArgsManager::AddCommand(const std::string& cmd, const std::string& help)
552 : {
553 0 : Assert(cmd.find('=') == std::string::npos);
554 0 : Assert(cmd.at(0) != '-');
555 :
556 0 : LOCK(cs_args);
557 0 : m_accept_any_command = false; // latch to false
558 0 : std::map<std::string, Arg>& arg_map = m_available_args[OptionsCategory::COMMANDS];
559 0 : auto ret = arg_map.emplace(cmd, Arg{"", help, ArgsManager::COMMAND});
560 0 : Assert(ret.second); // Fail on duplicate commands
561 0 : }
562 :
563 13110 : void ArgsManager::AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat)
564 : {
565 13110 : Assert((flags & ArgsManager::COMMAND) == 0); // use AddCommand
566 :
567 : // Split arg name from its help param
568 13110 : size_t eq_index = name.find('=');
569 13110 : if (eq_index == std::string::npos) {
570 6003 : eq_index = name.size();
571 6003 : }
572 13110 : std::string arg_name = name.substr(0, eq_index);
573 :
574 13110 : LOCK(cs_args);
575 13110 : std::map<std::string, Arg>& arg_map = m_available_args[cat];
576 13110 : auto ret = arg_map.emplace(arg_name, Arg{name.substr(eq_index, name.size() - eq_index), help, flags});
577 13110 : assert(ret.second); // Make sure an insertion actually happened
578 :
579 13110 : if (flags & ArgsManager::NETWORK_ONLY) {
580 552 : m_network_only_args.emplace(arg_name);
581 552 : }
582 13110 : }
583 :
584 276 : void ArgsManager::AddHiddenArgs(const std::vector<std::string>& names)
585 : {
586 2070 : for (const std::string& name : names) {
587 1794 : AddArg(name, "", ArgsManager::ALLOW_ANY, OptionsCategory::HIDDEN);
588 : }
589 276 : }
590 :
591 0 : std::string ArgsManager::GetHelpMessage() const
592 : {
593 0 : const bool show_debug = GetBoolArg("-help-debug", false);
594 :
595 0 : std::string usage;
596 0 : LOCK(cs_args);
597 0 : for (const auto& arg_map : m_available_args) {
598 0 : switch(arg_map.first) {
599 : case OptionsCategory::OPTIONS:
600 0 : usage += HelpMessageGroup("Options:");
601 0 : break;
602 : case OptionsCategory::CONNECTION:
603 0 : usage += HelpMessageGroup("Connection options:");
604 0 : break;
605 : case OptionsCategory::ZMQ:
606 0 : usage += HelpMessageGroup("ZeroMQ notification options:");
607 0 : break;
608 : case OptionsCategory::DEBUG_TEST:
609 0 : usage += HelpMessageGroup("Debugging/Testing options:");
610 0 : break;
611 : case OptionsCategory::NODE_RELAY:
612 0 : usage += HelpMessageGroup("Node relay options:");
613 0 : break;
614 : case OptionsCategory::BLOCK_CREATION:
615 0 : usage += HelpMessageGroup("Block creation options:");
616 0 : break;
617 : case OptionsCategory::RPC:
618 0 : usage += HelpMessageGroup("RPC server options:");
619 0 : break;
620 : case OptionsCategory::WALLET:
621 0 : usage += HelpMessageGroup("Wallet options:");
622 0 : break;
623 : case OptionsCategory::WALLET_DEBUG_TEST:
624 0 : if (show_debug) usage += HelpMessageGroup("Wallet debugging/testing options:");
625 0 : break;
626 : case OptionsCategory::CHAINPARAMS:
627 0 : usage += HelpMessageGroup("Chain selection options:");
628 0 : break;
629 : case OptionsCategory::GUI:
630 0 : usage += HelpMessageGroup("UI Options:");
631 0 : break;
632 : case OptionsCategory::COMMANDS:
633 0 : usage += HelpMessageGroup("Commands:");
634 0 : break;
635 : case OptionsCategory::REGISTER_COMMANDS:
636 0 : usage += HelpMessageGroup("Register Commands:");
637 0 : break;
638 : default:
639 0 : break;
640 : }
641 :
642 : // When we get to the hidden options, stop
643 0 : if (arg_map.first == OptionsCategory::HIDDEN) break;
644 :
645 0 : for (const auto& arg : arg_map.second) {
646 0 : if (show_debug || !(arg.second.m_flags & ArgsManager::DEBUG_ONLY)) {
647 0 : std::string name;
648 0 : if (arg.second.m_help_param.empty()) {
649 0 : name = arg.first;
650 0 : } else {
651 0 : name = arg.first + arg.second.m_help_param;
652 : }
653 0 : usage += HelpMessageOpt(name, arg.second.m_help_text);
654 0 : }
655 : }
656 : }
657 0 : return usage;
658 0 : }
659 :
660 0 : bool HelpRequested(const ArgsManager& args)
661 : {
662 0 : return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help") || args.IsArgSet("-help-debug");
663 0 : }
664 :
665 69 : void SetupHelpOptions(ArgsManager& args)
666 : {
667 69 : args.AddArg("-?", "Print this help message and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
668 69 : args.AddHiddenArgs({"-h", "-help"});
669 69 : }
670 :
671 : static const int screenWidth = 79;
672 : static const int optIndent = 2;
673 : static const int msgIndent = 7;
674 :
675 0 : std::string HelpMessageGroup(const std::string &message) {
676 0 : return std::string(message) + std::string("\n\n");
677 0 : }
678 :
679 0 : std::string HelpMessageOpt(const std::string &option, const std::string &message) {
680 0 : return std::string(optIndent,' ') + std::string(option) +
681 0 : std::string("\n") + std::string(msgIndent,' ') +
682 0 : FormatParagraph(message, screenWidth - msgIndent, msgIndent) +
683 0 : std::string("\n\n");
684 0 : }
685 :
686 0 : fs::path GetDefaultDataDir()
687 : {
688 : // Windows: C:\Users\Username\AppData\Roaming\Bitcoin
689 : // macOS: ~/Library/Application Support/Bitcoin
690 : // Unix-like: ~/.bitcoin
691 : #ifdef WIN32
692 : // Windows
693 : return GetSpecialFolderPath(CSIDL_APPDATA) / "Bitcoin";
694 : #else
695 0 : fs::path pathRet;
696 0 : char* pszHome = getenv("HOME");
697 0 : if (pszHome == nullptr || strlen(pszHome) == 0)
698 0 : pathRet = fs::path("/");
699 : else
700 0 : pathRet = fs::path(pszHome);
701 : #ifdef MAC_OSX
702 : // macOS
703 : return pathRet / "Library/Application Support/Bitcoin";
704 : #else
705 : // Unix-like
706 0 : return pathRet / ".bitcoin";
707 : #endif
708 : #endif
709 0 : }
710 :
711 0 : bool CheckDataDirOption(const ArgsManager& args)
712 : {
713 0 : const fs::path datadir{args.GetPathArg("-datadir")};
714 0 : return datadir.empty() || fs::is_directory(fs::absolute(datadir));
715 0 : }
716 :
717 0 : fs::path ArgsManager::GetConfigFilePath() const
718 : {
719 0 : LOCK(cs_args);
720 0 : return *Assert(m_config_path);
721 0 : }
722 :
723 69 : ChainType ArgsManager::GetChainType() const
724 : {
725 69 : std::variant<ChainType, std::string> arg = GetChainArg();
726 69 : if (auto* parsed = std::get_if<ChainType>(&arg)) return *parsed;
727 0 : throw std::runtime_error(strprintf("Unknown chain %s.", std::get<std::string>(arg)));
728 69 : }
729 :
730 0 : std::string ArgsManager::GetChainTypeString() const
731 : {
732 0 : auto arg = GetChainArg();
733 0 : if (auto* parsed = std::get_if<ChainType>(&arg)) return ChainTypeToString(*parsed);
734 0 : return std::get<std::string>(arg);
735 0 : }
736 :
737 69 : std::variant<ChainType, std::string> ArgsManager::GetChainArg() const
738 : {
739 276 : auto get_net = [&](const std::string& arg) {
740 207 : LOCK(cs_args);
741 207 : common::SettingsValue value = common::GetSetting(m_settings, /* section= */ "", SettingName(arg),
742 : /* ignore_default_section_config= */ false,
743 : /*ignore_nonpersistent=*/false,
744 : /* get_chain_type= */ true);
745 207 : return value.isNull() ? false : value.isBool() ? value.get_bool() : InterpretBool(value.get_str());
746 207 : };
747 :
748 69 : const bool fRegTest = get_net("-regtest");
749 69 : const bool fSigNet = get_net("-signet");
750 69 : const bool fTestNet = get_net("-testnet");
751 69 : const auto chain_arg = GetArg("-chain");
752 :
753 69 : if ((int)chain_arg.has_value() + (int)fRegTest + (int)fSigNet + (int)fTestNet > 1) {
754 0 : throw std::runtime_error("Invalid combination of -regtest, -signet, -testnet and -chain. Can use at most one.");
755 : }
756 69 : if (chain_arg) {
757 0 : if (auto parsed = ChainTypeFromString(*chain_arg)) return *parsed;
758 : // Not a known string, so return original string
759 0 : return *chain_arg;
760 : }
761 69 : if (fRegTest) return ChainType::REGTEST;
762 69 : if (fSigNet) return ChainType::SIGNET;
763 69 : if (fTestNet) return ChainType::TESTNET;
764 69 : return ChainType::MAIN;
765 69 : }
766 :
767 173196 : bool ArgsManager::UseDefaultSection(const std::string& arg) const
768 : {
769 173196 : return m_network == ChainTypeToString(ChainType::MAIN) || m_network_only_args.count(arg) == 0;
770 0 : }
771 :
772 172700 : common::SettingsValue ArgsManager::GetSetting(const std::string& arg) const
773 : {
774 172700 : LOCK(cs_args);
775 172700 : return common::GetSetting(
776 172700 : m_settings, m_network, SettingName(arg), !UseDefaultSection(arg),
777 : /*ignore_nonpersistent=*/false, /*get_chain_type=*/false);
778 172700 : }
779 :
780 496 : std::vector<common::SettingsValue> ArgsManager::GetSettingsList(const std::string& arg) const
781 : {
782 496 : LOCK(cs_args);
783 496 : return common::GetSettingsList(m_settings, m_network, SettingName(arg), !UseDefaultSection(arg));
784 496 : }
785 :
786 0 : void ArgsManager::logArgsPrefix(
787 : const std::string& prefix,
788 : const std::string& section,
789 : const std::map<std::string, std::vector<common::SettingsValue>>& args) const
790 : {
791 0 : std::string section_str = section.empty() ? "" : "[" + section + "] ";
792 0 : for (const auto& arg : args) {
793 0 : for (const auto& value : arg.second) {
794 0 : std::optional<unsigned int> flags = GetArgFlags('-' + arg.first);
795 0 : if (flags) {
796 0 : std::string value_str = (*flags & SENSITIVE) ? "****" : value.write();
797 0 : LogPrintf("%s %s%s=%s\n", prefix, section_str, arg.first, value_str);
798 0 : }
799 : }
800 : }
801 0 : }
802 :
803 0 : void ArgsManager::LogArgs() const
804 : {
805 0 : LOCK(cs_args);
806 0 : for (const auto& section : m_settings.ro_config) {
807 0 : logArgsPrefix("Config file arg:", section.first, section.second);
808 : }
809 0 : for (const auto& setting : m_settings.rw_settings) {
810 0 : LogPrintf("Setting file arg: %s = %s\n", setting.first, setting.second.write());
811 : }
812 0 : logArgsPrefix("Command-line arg:", "", m_settings.command_line_options);
813 0 : }
814 :
815 : namespace common {
816 : #ifdef WIN32
817 : WinCmdLineArgs::WinCmdLineArgs()
818 : {
819 : wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
820 : std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> utf8_cvt;
821 : argv = new char*[argc];
822 : args.resize(argc);
823 : for (int i = 0; i < argc; i++) {
824 : args[i] = utf8_cvt.to_bytes(wargv[i]);
825 : argv[i] = &*args[i].begin();
826 : }
827 : LocalFree(wargv);
828 : }
829 :
830 : WinCmdLineArgs::~WinCmdLineArgs()
831 : {
832 : delete[] argv;
833 : }
834 :
835 : std::pair<int, char**> WinCmdLineArgs::get()
836 : {
837 : return std::make_pair(argc, argv);
838 : }
839 : #endif
840 : } // namespace common
|