args.h raw
1 // Copyright (c) 2023-present The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5 #ifndef BITCOIN_COMMON_ARGS_H
6 #define BITCOIN_COMMON_ARGS_H
7
8 #include <common/settings.h>
9 #include <compat/compat.h>
10 #include <sync.h>
11 #include <util/chaintype.h>
12 #include <util/fs.h>
13
14 #include <concepts>
15 #include <cstdint>
16 #include <iosfwd>
17 #include <list>
18 #include <map>
19 #include <optional>
20 #include <set>
21 #include <string>
22 #include <string_view>
23 #include <variant>
24 #include <vector>
25
26 class ArgsManager;
27
28 extern const char * const BITCOIN_CONF_FILENAME;
29 extern const char * const BITCOIN_SETTINGS_FILENAME;
30
31 // Return true if -datadir option points to a valid directory or is not specified.
32 bool CheckDataDirOption(const ArgsManager& args);
33
34 /**
35 * Most paths passed as configuration arguments are treated as relative to
36 * the datadir if they are not absolute.
37 *
38 * @param args Parsed arguments and settings.
39 * @param path The path to be conditionally prefixed with datadir.
40 * @param net_specific Use network specific datadir variant
41 * @return The normalized path.
42 */
43 fs::path AbsPathForConfigVal(const ArgsManager& args, const fs::path& path, bool net_specific = true);
44
45 inline bool IsSwitchChar(char c)
46 {
47 #ifdef WIN32
48 return c == '-' || c == '/';
49 #else
50 return c == '-';
51 #endif
52 }
53
54 enum class OptionsCategory {
55 OPTIONS,
56 CONNECTION,
57 WALLET,
58 WALLET_DEBUG_TEST,
59 ZMQ,
60 DEBUG_TEST,
61 CHAINPARAMS,
62 NODE_RELAY,
63 BLOCK_CREATION,
64 RPC,
65 GUI,
66 COMMANDS,
67 REGISTER_COMMANDS,
68 CLI_COMMANDS,
69 IPC,
70
71 // Specific to one or more commands (OptionsCategory::COMMANDS)
72 // These are only included in help with their associated commands.
73 COMMAND_OPTIONS,
74
75 HIDDEN // Always the last option to avoid printing these in the help
76 };
77
78 struct KeyInfo {
79 std::string name;
80 std::string section;
81 bool negated{false};
82 };
83
84 KeyInfo InterpretKey(std::string key);
85
86 std::optional<common::SettingsValue> InterpretValue(const KeyInfo& key, const std::string* value,
87 unsigned int flags, std::string& error);
88
89 struct SectionInfo {
90 std::string m_name;
91 std::string m_file;
92 int m_line;
93 };
94
95 std::string SettingToString(const common::SettingsValue&, const std::string&);
96 std::optional<std::string> SettingToString(const common::SettingsValue&);
97
98 template <std::integral Int>
99 Int SettingTo(const common::SettingsValue&, Int);
100
101 template <std::integral Int>
102 std::optional<Int> SettingTo(const common::SettingsValue&);
103
104 bool SettingToBool(const common::SettingsValue&, bool);
105 std::optional<bool> SettingToBool(const common::SettingsValue&);
106
107 class ArgsManager
108 {
109 public:
110 /**
111 * Flags controlling how config and command line arguments are validated and
112 * interpreted.
113 */
114 enum Flags : uint32_t {
115 ALLOW_ANY = 0x01, //!< disable validation
116 // ALLOW_BOOL = 0x02, //!< unimplemented, draft implementation in #16545
117 // ALLOW_INT = 0x04, //!< unimplemented, draft implementation in #16545
118 // ALLOW_STRING = 0x08, //!< unimplemented, draft implementation in #16545
119 // ALLOW_LIST = 0x10, //!< unimplemented, draft implementation in #16545
120 DISALLOW_NEGATION = 0x20, //!< disallow -nofoo syntax
121 DISALLOW_ELISION = 0x40, //!< disallow -foo syntax that doesn't assign any value
122
123 DEBUG_ONLY = 0x100,
124 /* Some options would cause cross-contamination if values for
125 * mainnet were used while running on regtest/testnet (or vice-versa).
126 * Setting them as NETWORK_ONLY ensures that sharing a config file
127 * between mainnet and regtest/testnet won't cause problems due to these
128 * parameters by accident. */
129 NETWORK_ONLY = 0x200,
130 // This argument's value is sensitive (such as a password).
131 SENSITIVE = 0x400,
132 COMMAND = 0x800,
133 };
134
135 private:
136 struct Arg
137 {
138 std::string m_help_param;
139 std::string m_help_text;
140 unsigned int m_flags;
141 };
142
143 mutable Mutex cs_args;
144 common::Settings m_settings GUARDED_BY(cs_args);
145 std::vector<std::string> m_command GUARDED_BY(cs_args);
146 std::string m_network GUARDED_BY(cs_args);
147 std::set<std::string> m_network_only_args GUARDED_BY(cs_args);
148 std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args GUARDED_BY(cs_args);
149 std::optional<unsigned int> m_default_flags GUARDED_BY(cs_args){};
150 std::map<std::string, std::set<std::string>> m_command_args GUARDED_BY(cs_args);
151 bool m_accept_any_command GUARDED_BY(cs_args){true};
152 std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args);
153 std::optional<fs::path> m_config_path GUARDED_BY(cs_args);
154 mutable fs::path m_cached_blocks_path GUARDED_BY(cs_args);
155 mutable fs::path m_cached_datadir_path GUARDED_BY(cs_args);
156 mutable fs::path m_cached_network_datadir_path GUARDED_BY(cs_args);
157
158 /**
159 * Returns true if settings values from the default section should be used,
160 * depending on the current network and whether the setting is
161 * network-specific.
162 */
163 bool UseDefaultSection(const std::string& arg) const EXCLUSIVE_LOCKS_REQUIRED(cs_args);
164
165 protected:
166 [[nodiscard]] bool ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys = false) EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
167 [[nodiscard]] bool ReadConfigString(const std::string& str_config) EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
168
169 public:
170 /**
171 * Get setting value.
172 *
173 * Result will be null if setting was unset, true if "-setting" argument was passed
174 * false if "-nosetting" argument was passed, and a string if a "-setting=value"
175 * argument was passed.
176 */
177 common::SettingsValue GetSetting(const std::string& arg) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
178
179 /**
180 * Get list of setting values.
181 */
182 std::vector<common::SettingsValue> GetSettingsList(const std::string& arg) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
183
184 ArgsManager();
185 ~ArgsManager();
186
187 /**
188 * Select the network in use
189 */
190 void SelectConfigNetwork(const std::string& network) EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
191
192 [[nodiscard]] bool ParseParameters(int argc, const char* const argv[], std::string& error) EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
193
194 /**
195 * Return config file path (read-only)
196 */
197 fs::path GetConfigFilePath() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
198 void SetConfigFilePath(fs::path) EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
199 [[nodiscard]] bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false) EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
200
201 /**
202 * Log warnings for options in m_section_only_args when
203 * they are specified in the default section but not overridden
204 * on the command line or in a network-specific section in the
205 * config file.
206 */
207 std::set<std::string> GetUnsuitableSectionOnlyArgs() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
208
209 /**
210 * Log warnings for unrecognized section names in the config file.
211 */
212 std::list<SectionInfo> GetUnrecognizedSections() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
213
214 struct Command {
215 /** The command (if one has been registered with AddCommand), or empty */
216 std::string command;
217 /**
218 * If command is non-empty: Any args that followed it
219 * If command is empty: The unregistered command and any args that followed it
220 */
221 std::vector<std::string> args;
222 };
223 /**
224 * Get the command and command args (returns std::nullopt if no command provided)
225 */
226 std::optional<const Command> GetCommand() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
227
228 /**
229 * Check that any command-specific options the user specified are valid
230 * for the given command.
231 *
232 * @param[in] command The command being run.
233 * @param[out] errors If non-null, populated with a message for each invalid option.
234 * @return false if any command-specific options were specified that are not valid for this command
235 */
236 bool CheckCommandOptions(const std::string& command, std::vector<std::string>* errors = nullptr) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
237
238 /**
239 * Get blocks directory path
240 *
241 * @return Blocks path which is network specific
242 */
243 fs::path GetBlocksDirPath() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
244
245 /**
246 * Get data directory path
247 *
248 * @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
249 */
250 fs::path GetDataDirBase() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
251
252 /**
253 * Get data directory path with appended network identifier
254 *
255 * @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
256 */
257 fs::path GetDataDirNet() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
258
259 /**
260 * Clear cached directory paths
261 */
262 void ClearPathCache() EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
263
264 /**
265 * Return a vector of strings of the given argument
266 *
267 * @param strArg Argument to get (e.g. "-foo")
268 * @return command-line arguments
269 */
270 std::vector<std::string> GetArgs(const std::string& strArg) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
271
272 /**
273 * Return true if the given argument has been manually set
274 *
275 * @param strArg Argument to get (e.g. "-foo")
276 * @return true if the argument has been set
277 */
278 bool IsArgSet(const std::string& strArg) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
279
280 /**
281 * Return true if the argument was originally passed as a negated option,
282 * i.e. -nofoo.
283 *
284 * @param strArg Argument to get (e.g. "-foo")
285 * @return true if the argument was passed negated
286 */
287 bool IsArgNegated(const std::string& strArg) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
288
289 /**
290 * Return string argument or default value
291 *
292 * @param strArg Argument to get (e.g. "-foo")
293 * @param strDefault (e.g. "1")
294 * @return command-line argument or default value
295 */
296 std::string GetArg(const std::string& strArg, const std::string& strDefault) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
297 std::optional<std::string> GetArg(const std::string& strArg) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
298
299 /**
300 * Return path argument or default value
301 *
302 * @param arg Argument to get a path from (e.g., "-datadir", "-blocksdir" or "-walletdir")
303 * @param default_value Optional default value to return instead of the empty path.
304 * @return normalized path if argument is set, with redundant "." and ".."
305 * path components and trailing separators removed (see patharg unit test
306 * for examples or implementation for details). If argument is empty or not
307 * set, default_value is returned unchanged.
308 */
309 fs::path GetPathArg(std::string arg, const fs::path& default_value = {}) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
310
311 /**
312 * Return integer argument or default value
313 *
314 * @param strArg Argument to get (e.g. "-foo")
315 * @param nDefault (e.g. 1)
316 * @return command-line argument (0 if invalid number) or default value
317 */
318 template <std::integral Int>
319 Int GetArg(const std::string& strArg, Int nDefault) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
320
321 template <std::integral Int>
322 std::optional<Int> GetArg(const std::string& strArg) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
323
324 int64_t GetIntArg(const std::string& strArg, int64_t nDefault) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args) { return GetArg<int64_t>(strArg, nDefault); }
325 std::optional<int64_t> GetIntArg(const std::string& strArg) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args) { return GetArg<int64_t>(strArg); }
326
327 /**
328 * Return boolean argument or default value
329 *
330 * @param strArg Argument to get (e.g. "-foo")
331 * @param fDefault (true or false)
332 * @return command-line argument or default value
333 */
334 bool GetBoolArg(const std::string& strArg, bool fDefault) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
335 std::optional<bool> GetBoolArg(const std::string& strArg) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
336
337 /**
338 * Set an argument if it doesn't already have a value
339 *
340 * @param strArg Argument to set (e.g. "-foo")
341 * @param strValue Value (e.g. "1")
342 * @return true if argument gets set, false if it already had a value
343 */
344 bool SoftSetArg(const std::string& strArg, const std::string& strValue) EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
345
346 /**
347 * Set a boolean argument if it doesn't already have a value
348 *
349 * @param strArg Argument to set (e.g. "-foo")
350 * @param fValue Value (e.g. false)
351 * @return true if argument gets set, false if it already had a value
352 */
353 bool SoftSetBoolArg(const std::string& strArg, bool fValue) EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
354
355 // Forces an arg setting. Called by SoftSetArg() if the arg hasn't already
356 // been set. Also called directly in testing.
357 void ForceSetArg(const std::string& strArg, const std::string& strValue) EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
358
359 /**
360 * Returns the appropriate chain type from the program arguments.
361 * @return ChainType::MAIN by default; raises runtime error if an invalid
362 * combination, or unknown chain is given.
363 */
364 ChainType GetChainType() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
365
366 /**
367 * Returns the appropriate chain type string from the program arguments.
368 * @return ChainType::MAIN string by default; raises runtime error if an
369 * invalid combination is given.
370 */
371 std::string GetChainTypeString() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
372
373 /**
374 * Add argument
375 */
376 void AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat) EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
377
378 /**
379 * Add command
380 */
381 void AddCommand(const std::string& cmd, const std::string& help, std::set<std::string> options = {}) EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
382
383 /**
384 * Add many hidden arguments
385 */
386 void AddHiddenArgs(const std::vector<std::string>& args) EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
387
388 /**
389 * Clear available arguments
390 */
391 void ClearArgs() EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
392
393 /**
394 * Check CLI command args
395 *
396 * @throws std::runtime_error when multiple CLI_COMMAND arguments are specified
397 */
398 void CheckMultipleCLIArgs() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
399
400 /**
401 * Get the help string
402 */
403 std::string GetHelpMessage() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
404
405 /**
406 * Return Flags for known arg.
407 * Return default flags for unknown arg.
408 */
409 std::optional<unsigned int> GetArgFlags(const std::string& name) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
410
411 /**
412 * Set default flags to return for an unknown arg.
413 */
414 void SetDefaultFlags(std::optional<unsigned int>) EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
415
416 /**
417 * Get settings file path, or return false if read-write settings were
418 * disabled with -nosettings.
419 */
420 bool GetSettingsPath(fs::path* filepath = nullptr, bool temp = false, bool backup = false) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
421
422 /**
423 * Read settings file. Push errors to vector, or log them if null.
424 */
425 bool ReadSettingsFile(std::vector<std::string>* errors = nullptr) EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
426
427 /**
428 * Write settings file or backup settings file. Push errors to vector, or
429 * log them if null.
430 */
431 bool WriteSettingsFile(std::vector<std::string>* errors = nullptr, bool backup = false) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
432
433 /**
434 * Get current setting from config file or read/write settings file,
435 * ignoring nonpersistent command line or forced settings values.
436 */
437 common::SettingsValue GetPersistentSetting(const std::string& name) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
438
439 /**
440 * Access settings with lock held.
441 */
442 template <typename Fn>
443 void LockSettings(Fn&& fn) EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
444 {
445 LOCK(cs_args);
446 fn(m_settings);
447 }
448
449 /**
450 * Log the config file options and the command line arguments,
451 * useful for troubleshooting.
452 */
453 void LogArgs() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
454
455 private:
456 // Internal helpers, for use by callers that already hold `cs_args`.
457 common::SettingsValue GetSetting_(const std::string& arg) const EXCLUSIVE_LOCKS_REQUIRED(cs_args);
458 std::optional<unsigned int> GetArgFlags_(const std::string& name) const EXCLUSIVE_LOCKS_REQUIRED(cs_args);
459 fs::path GetPathArg_(std::string arg, const fs::path& default_value = {}) const EXCLUSIVE_LOCKS_REQUIRED(cs_args);
460
461 /**
462 * Get data directory path
463 *
464 * @param net_specific Append network identifier to the returned path
465 * @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
466 */
467 fs::path GetDataDir(bool net_specific) const EXCLUSIVE_LOCKS_REQUIRED(cs_args);
468
469 /**
470 * Return -regtest/-signet/-testnet/-testnet4/-chain= setting as a ChainType enum if a
471 * recognized chain type was set, or as a string if an unrecognized chain
472 * name was set. Raise an exception if an invalid combination of flags was
473 * provided.
474 */
475 std::variant<ChainType, std::string> GetChainArg() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
476
477 // Helper function for LogArgs().
478 void logArgsPrefix(
479 const std::string& prefix,
480 const std::string& section,
481 const std::map<std::string, std::vector<common::SettingsValue>>& args) const EXCLUSIVE_LOCKS_REQUIRED(cs_args);
482 };
483
484 extern ArgsManager gArgs;
485
486 /**
487 * @return true if help has been requested via a command-line arg
488 */
489 bool HelpRequested(const ArgsManager& args);
490
491 /** Add help options to the args manager */
492 void SetupHelpOptions(ArgsManager& args);
493
494 extern const std::vector<std::string> TEST_OPTIONS_DOC;
495
496 /** Checks if a particular test option is present in -test command-line arg options */
497 bool HasTestOption(const ArgsManager& args, const std::string& test_option);
498
499 /**
500 * Format a string to be used as group of options in help messages
501 *
502 * @param message Group name (e.g. "RPC server options:")
503 * @return the formatted string
504 */
505 std::string HelpMessageGroup(const std::string& message);
506
507 /**
508 * Format a string to be used as option description in help messages
509 *
510 * @param option Option name (e.g. "-rpcuser")
511 * @param help_param Help parameter (e.g. "=<user>" or "")
512 * @param message Option description (e.g. "Username for JSON-RPC connections")
513 * @param subopt True if this is a suboption, instead of a top-level option.
514 * @return the formatted string
515 */
516 std::string HelpMessageOpt(std::string_view option, std::string_view help_param, std::string_view message, bool subopt = false);
517
518 #endif // BITCOIN_COMMON_ARGS_H
519