/ src / common / args.h
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