args.h raw

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