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