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