Bitcoin ABC  0.26.3
P2P Digital Currency
configfile.cpp
Go to the documentation of this file.
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 #include <common/args.h>
6 
7 #include <logging.h>
8 #include <sync.h>
9 #include <tinyformat.h>
10 #include <univalue.h>
11 #include <util/fs.h>
12 #include <util/settings.h>
13 #include <util/string.h>
14 
15 #include <algorithm>
16 #include <cassert>
17 #include <cstdlib>
18 #include <fstream>
19 #include <iostream>
20 #include <list>
21 #include <map>
22 #include <memory>
23 #include <optional>
24 #include <string>
25 #include <string_view>
26 #include <utility>
27 #include <vector>
28 
30  const fs::path &configuration_file_path) {
31  return AbsPathForConfigVal(args, configuration_file_path,
32  /*net_specific=*/false);
33 }
34 
35 static bool
36 GetConfigOptions(std::istream &stream, const std::string &filepath,
37  std::string &error,
38  std::vector<std::pair<std::string, std::string>> &options,
39  std::list<SectionInfo> &sections) {
40  std::string str, prefix;
41  std::string::size_type pos;
42  int linenr = 1;
43  while (std::getline(stream, str)) {
44  bool used_hash = false;
45  if ((pos = str.find('#')) != std::string::npos) {
46  str = str.substr(0, pos);
47  used_hash = true;
48  }
49  const static std::string pattern = " \t\r\n";
50  str = TrimString(str, pattern);
51  if (!str.empty()) {
52  if (*str.begin() == '[' && *str.rbegin() == ']') {
53  const std::string section = str.substr(1, str.size() - 2);
54  sections.emplace_back(SectionInfo{section, filepath, linenr});
55  prefix = section + '.';
56  } else if (*str.begin() == '-') {
57  error = strprintf(
58  "parse error on line %i: %s, options in configuration file "
59  "must be specified without leading -",
60  linenr, str);
61  return false;
62  } else if ((pos = str.find('=')) != std::string::npos) {
63  std::string name =
64  prefix + TrimString(str.substr(0, pos), pattern);
65  std::string value = TrimString(str.substr(pos + 1), pattern);
66  if (used_hash &&
67  name.find("rpcpassword") != std::string::npos) {
68  error = strprintf(
69  "parse error on line %i, using # in rpcpassword can be "
70  "ambiguous and should be avoided",
71  linenr);
72  return false;
73  }
74  options.emplace_back(name, value);
75  if ((pos = name.rfind('.')) != std::string::npos &&
76  prefix.length() <= pos) {
77  sections.emplace_back(
78  SectionInfo{name.substr(0, pos), filepath, linenr});
79  }
80  } else {
81  error = strprintf("parse error on line %i: %s", linenr, str);
82  if (str.size() >= 2 && str.substr(0, 2) == "no") {
83  error += strprintf(", if you intended to specify a negated "
84  "option, use %s=1 instead",
85  str);
86  }
87  return false;
88  }
89  }
90  ++linenr;
91  }
92  return true;
93 }
94 
95 bool ArgsManager::ReadConfigStream(std::istream &stream,
96  const std::string &filepath,
97  std::string &error,
98  bool ignore_invalid_keys) {
99  LOCK(cs_args);
100  std::vector<std::pair<std::string, std::string>> options;
101  if (!GetConfigOptions(stream, filepath, error, options,
102  m_config_sections)) {
103  return false;
104  }
105  for (const std::pair<std::string, std::string> &option : options) {
106  std::string section;
107  std::string key = option.first;
108  util::SettingsValue value =
109  InterpretOption(section, key, option.second);
110  std::optional<unsigned int> flags = GetArgFlags('-' + key);
111  if (flags) {
112  if (!CheckValid(key, value, *flags, error)) {
113  return false;
114  }
115  m_settings.ro_config[section][key].push_back(value);
116  } else {
117  if (ignore_invalid_keys) {
118  LogPrintf("Ignoring unknown configuration value %s\n",
119  option.first);
120  } else {
121  error = strprintf("Invalid configuration value %s",
122  option.first.c_str());
123  return false;
124  }
125  }
126  }
127  return true;
128 }
129 
131  bool ignore_invalid_keys) {
132  {
133  LOCK(cs_args);
134  m_settings.ro_config.clear();
135  m_config_sections.clear();
136  }
137 
138  const auto conf_path{GetConfigFilePath()};
139  std::ifstream stream{conf_path};
140 
141  // ok to not have a config file
142  if (stream.good()) {
143  if (!ReadConfigStream(stream, fs::PathToString(conf_path), error,
144  ignore_invalid_keys)) {
145  return false;
146  }
147  // `-includeconf` cannot be included in the command line arguments
148  // except as `-noincludeconf` (which indicates that no included conf
149  // file should be used).
150  bool use_conf_file{true};
151  {
152  LOCK(cs_args);
153  if (auto *includes = util::FindKey(m_settings.command_line_options,
154  "includeconf")) {
155  // ParseParameters() fails if a non-negated -includeconf is
156  // passed on the command-line
157  assert(util::SettingsSpan(*includes).last_negated());
158  use_conf_file = false;
159  }
160  }
161  if (use_conf_file) {
162  std::string chain_id = GetChainName();
163  std::vector<std::string> conf_file_names;
164 
165  auto add_includes = [&](const std::string &network,
166  size_t skip = 0) {
167  size_t num_values = 0;
168  LOCK(cs_args);
169  if (auto *section =
170  util::FindKey(m_settings.ro_config, network)) {
171  if (auto *values = util::FindKey(*section, "includeconf")) {
172  for (size_t i = std::max(
173  skip, util::SettingsSpan(*values).negated());
174  i < values->size(); ++i) {
175  conf_file_names.push_back((*values)[i].get_str());
176  }
177  num_values = values->size();
178  }
179  }
180  return num_values;
181  };
182 
183  // We haven't set m_network yet (that happens in SelectParams()), so
184  // manually check for network.includeconf args.
185  const size_t chain_includes = add_includes(chain_id);
186  const size_t default_includes = add_includes({});
187 
188  for (const std::string &conf_file_name : conf_file_names) {
189  std::ifstream conf_file_stream{
190  GetConfigFile(*this, fs::PathFromString(conf_file_name))};
191  if (conf_file_stream.good()) {
192  if (!ReadConfigStream(conf_file_stream, conf_file_name,
193  error, ignore_invalid_keys)) {
194  return false;
195  }
196  LogPrintf("Included configuration file %s\n",
197  conf_file_name);
198  } else {
199  error = "Failed to include configuration file " +
200  conf_file_name;
201  return false;
202  }
203  }
204 
205  // Warn about recursive -includeconf
206  conf_file_names.clear();
207  add_includes(chain_id, /* skip= */ chain_includes);
208  add_includes({}, /* skip= */ default_includes);
209  std::string chain_id_final = GetChainName();
210  if (chain_id_final != chain_id) {
211  // Also warn about recursive includeconf for the chain that was
212  // specified in one of the includeconfs
213  add_includes(chain_id_final);
214  }
215  for (const std::string &conf_file_name : conf_file_names) {
216  tfm::format(std::cerr,
217  "warning: -includeconf cannot be used from "
218  "included files; ignoring -includeconf=%s\n",
219  conf_file_name);
220  }
221  }
222  }
223 
224  // If datadir is changed in .conf file:
225  ClearPathCache();
226  if (!CheckDataDirOption(*this)) {
227  error = strprintf("specified data directory \"%s\" does not exist.",
228  GetArg("-datadir", "").c_str());
229  return false;
230  }
231  return true;
232 }
233 
235  bool net_specific) {
236  if (path.is_absolute()) {
237  return path;
238  }
239  return fsbridge::AbsPathJoin(
240  net_specific ? args.GetDataDirNet() : args.GetDataDirBase(), path);
241 }
bool CheckValid(const std::string &key, const util::SettingsValue &val, unsigned int flags, std::string &error)
Check settings value validity according to flags.
Definition: args.cpp:117
bool CheckDataDirOption(const ArgsManager &args)
Definition: args.cpp:784
util::SettingsValue InterpretOption(std::string &section, std::string &key, const std::string &value)
Interpret -nofoo as if the user supplied -foo=0.
Definition: args.cpp:87
int flags
Definition: bitcoin-tx.cpp:543
std::optional< unsigned int > GetArgFlags(const std::string &name) const
Return Flags for known arg.
Definition: args.cpp:264
const fs::path & GetDataDirBase() const
Get data directory path.
Definition: args.h:206
void ClearPathCache()
Clear cached directory paths.
Definition: args.cpp:363
const fs::path & GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: args.h:215
fs::path GetConfigFilePath() const
Return config file path (read-only)
Definition: args.cpp:789
RecursiveMutex cs_args
Definition: args.h:122
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: args.cpp:494
bool ReadConfigStream(std::istream &stream, const std::string &filepath, std::string &error, bool ignore_invalid_keys=false)
Definition: configfile.cpp:95
bool ReadConfigFiles(std::string &error, bool ignore_invalid_keys=false)
Definition: configfile.cpp:130
std::string GetChainName() const
Looks for -regtest, -testnet and returns the appropriate BIP70 chain name.
Definition: args.cpp:793
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:30
static bool GetConfigOptions(std::istream &stream, const std::string &filepath, std::string &error, std::vector< std::pair< std::string, std::string >> &options, std::list< SectionInfo > &sections)
Definition: configfile.cpp:36
fs::path AbsPathForConfigVal(const ArgsManager &args, const fs::path &path, bool net_specific)
Most paths passed as configuration arguments are treated as relative to the datadir if they are not a...
Definition: configfile.cpp:234
fs::path GetConfigFile(const ArgsManager &args, const fs::path &configuration_file_path)
Definition: configfile.cpp:29
bool error(const char *fmt, const Args &...args)
Definition: logging.h:226
#define LogPrintf(...)
Definition: logging.h:207
static std::string PathToString(const path &path)
Convert path object to byte string.
Definition: fs.h:142
static path PathFromString(const std::string &string)
Convert byte string to path object.
Definition: fs.h:165
fs::path AbsPathJoin(const fs::path &base, const fs::path &path)
Helper function for joining two paths.
Definition: fs.cpp:39
void format(std::ostream &out, const char *fmt, const Args &...args)
Format list of arguments to the stream according to given format string.
Definition: tinyformat.h:1112
auto FindKey(Map &&map, Key &&key) -> decltype(&map.at(key))
Map lookup helper.
Definition: settings.h:115
const char * prefix
Definition: rest.cpp:817
const char * name
Definition: rest.cpp:47
std::string TrimString(const std::string &str, const std::string &pattern=" \f\n\r\t\v")
Definition: string.h:28
Accessor for list of settings that skips negated values when iterated over.
Definition: settings.h:91
size_t negated() const
Number of negated values.
Definition: settings.cpp:296
bool last_negated() const
True if the last value is negated.
Definition: settings.cpp:293
#define LOCK(cs)
Definition: sync.h:306
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1202
assert(!tx.IsCoinBase())