Bitcoin Core  0.18.99
P2P Digital Currency
system.cpp
Go to the documentation of this file.
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2018 The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include <util/system.h>
7 
8 #include <chainparamsbase.h>
9 #include <util/strencodings.h>
10 
11 #include <stdarg.h>
12 
13 #if (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__))
14 #include <pthread.h>
15 #include <pthread_np.h>
16 #endif
17 
18 #ifndef WIN32
19 // for posix_fallocate
20 #ifdef __linux__
21 
22 #ifdef _POSIX_C_SOURCE
23 #undef _POSIX_C_SOURCE
24 #endif
25 
26 #define _POSIX_C_SOURCE 200112L
27 
28 #endif // __linux__
29 
30 #include <algorithm>
31 #include <fcntl.h>
32 #include <sched.h>
33 #include <sys/resource.h>
34 #include <sys/stat.h>
35 
36 #else
37 
38 #ifdef _MSC_VER
39 #pragma warning(disable:4786)
40 #pragma warning(disable:4804)
41 #pragma warning(disable:4805)
42 #pragma warning(disable:4717)
43 #endif
44 
45 #ifdef _WIN32_IE
46 #undef _WIN32_IE
47 #endif
48 #define _WIN32_IE 0x0501
49 
50 #define WIN32_LEAN_AND_MEAN 1
51 #ifndef NOMINMAX
52 #define NOMINMAX
53 #endif
54 #include <codecvt>
55 
56 #include <io.h> /* for _commit */
57 #include <shellapi.h>
58 #include <shlobj.h>
59 #endif
60 
61 #ifdef HAVE_MALLOPT_ARENA_MAX
62 #include <malloc.h>
63 #endif
64 
65 #include <thread>
66 
67 // Application startup time (used for uptime calculation)
68 const int64_t nStartupTime = GetTime();
69 
70 const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
71 
73 
79 static std::map<std::string, std::unique_ptr<fsbridge::FileLock>> dir_locks;
81 static std::mutex cs_dir_locks;
82 
83 bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only)
84 {
85  std::lock_guard<std::mutex> ulock(cs_dir_locks);
86  fs::path pathLockFile = directory / lockfile_name;
87 
88  // If a lock for this directory already exists in the map, don't try to re-lock it
89  if (dir_locks.count(pathLockFile.string())) {
90  return true;
91  }
92 
93  // Create empty lock file if it doesn't exist.
94  FILE* file = fsbridge::fopen(pathLockFile, "a");
95  if (file) fclose(file);
96  auto lock = MakeUnique<fsbridge::FileLock>(pathLockFile);
97  if (!lock->TryLock()) {
98  return error("Error while attempting to lock directory %s: %s", directory.string(), lock->GetReason());
99  }
100  if (!probe_only) {
101  // Lock successful and we're not just probing, put it into the map
102  dir_locks.emplace(pathLockFile.string(), std::move(lock));
103  }
104  return true;
105 }
106 
107 void UnlockDirectory(const fs::path& directory, const std::string& lockfile_name)
108 {
109  std::lock_guard<std::mutex> lock(cs_dir_locks);
110  dir_locks.erase((directory / lockfile_name).string());
111 }
112 
114 {
115  std::lock_guard<std::mutex> ulock(cs_dir_locks);
116  dir_locks.clear();
117 }
118 
119 bool DirIsWritable(const fs::path& directory)
120 {
121  fs::path tmpFile = directory / fs::unique_path();
122 
123  FILE* file = fsbridge::fopen(tmpFile, "a");
124  if (!file) return false;
125 
126  fclose(file);
127  remove(tmpFile);
128 
129  return true;
130 }
131 
132 bool CheckDiskSpace(const fs::path& dir, uint64_t additional_bytes)
133 {
134  constexpr uint64_t min_disk_space = 52428800; // 50 MiB
135 
136  uint64_t free_bytes_available = fs::space(dir).available;
137  return free_bytes_available >= min_disk_space + additional_bytes;
138 }
139 
157 static bool InterpretBool(const std::string& strValue)
158 {
159  if (strValue.empty())
160  return true;
161  return (atoi(strValue) != 0);
162 }
163 
166 public:
167  typedef std::map<std::string, std::vector<std::string>> MapArgs;
168 
171  static inline bool UseDefaultSection(const ArgsManager& am, const std::string& arg) EXCLUSIVE_LOCKS_REQUIRED(am.cs_args)
172  {
173  return (am.m_network == CBaseChainParams::MAIN || am.m_network_only_args.count(arg) == 0);
174  }
175 
177  static inline std::string NetworkArg(const ArgsManager& am, const std::string& arg)
178  {
179  assert(arg.length() > 1 && arg[0] == '-');
180  return "-" + am.m_network + "." + arg.substr(1);
181  }
182 
184  static inline void AddArgs(std::vector<std::string>& res, const MapArgs& map_args, const std::string& arg)
185  {
186  auto it = map_args.find(arg);
187  if (it != map_args.end()) {
188  res.insert(res.end(), it->second.begin(), it->second.end());
189  }
190  }
191 
195  static inline std::pair<bool,std::string> GetArgHelper(const MapArgs& map_args, const std::string& arg, bool getLast = false)
196  {
197  auto it = map_args.find(arg);
198 
199  if (it == map_args.end() || it->second.empty()) {
200  return std::make_pair(false, std::string());
201  }
202 
203  if (getLast) {
204  return std::make_pair(true, it->second.back());
205  } else {
206  return std::make_pair(true, it->second.front());
207  }
208  }
209 
210  /* Get the string value of an argument, returning a pair of a boolean
211  * indicating the argument was found, and the value for the argument
212  * if it was found (or the empty string if not found).
213  */
214  static inline std::pair<bool,std::string> GetArg(const ArgsManager &am, const std::string& arg)
215  {
216  LOCK(am.cs_args);
217  std::pair<bool,std::string> found_result(false, std::string());
218 
219  // We pass "true" to GetArgHelper in order to return the last
220  // argument value seen from the command line (so "bitcoind -foo=bar
221  // -foo=baz" gives GetArg(am,"foo")=={true,"baz"}
222  found_result = GetArgHelper(am.m_override_args, arg, true);
223  if (found_result.first) {
224  return found_result;
225  }
226 
227  // But in contrast we return the first argument seen in a config file,
228  // so "foo=bar \n foo=baz" in the config file gives
229  // GetArg(am,"foo")={true,"bar"}
230  if (!am.m_network.empty()) {
231  found_result = GetArgHelper(am.m_config_args, NetworkArg(am, arg));
232  if (found_result.first) {
233  return found_result;
234  }
235  }
236 
237  if (UseDefaultSection(am, arg)) {
238  found_result = GetArgHelper(am.m_config_args, arg);
239  if (found_result.first) {
240  return found_result;
241  }
242  }
243 
244  return found_result;
245  }
246 
247  /* Special test for -testnet and -regtest args, because we
248  * don't want to be confused by craziness like "[regtest] testnet=1"
249  */
250  static inline bool GetNetBoolArg(const ArgsManager &am, const std::string& net_arg) EXCLUSIVE_LOCKS_REQUIRED(am.cs_args)
251  {
252  std::pair<bool,std::string> found_result(false,std::string());
253  found_result = GetArgHelper(am.m_override_args, net_arg, true);
254  if (!found_result.first) {
255  found_result = GetArgHelper(am.m_config_args, net_arg, true);
256  if (!found_result.first) {
257  return false; // not set
258  }
259  }
260  return InterpretBool(found_result.second); // is set, so evaluate
261  }
262 };
263 
285 static bool InterpretNegatedOption(std::string& key, std::string& val)
286 {
287  assert(key[0] == '-');
288 
289  size_t option_index = key.find('.');
290  if (option_index == std::string::npos) {
291  option_index = 1;
292  } else {
293  ++option_index;
294  }
295  if (key.substr(option_index, 2) == "no") {
296  bool bool_val = InterpretBool(val);
297  key.erase(option_index, 2);
298  if (!bool_val ) {
299  // Double negatives like -nofoo=0 are supported (but discouraged)
300  LogPrintf("Warning: parsed potentially confusing double-negative %s=%s\n", key, val);
301  val = "1";
302  } else {
303  return true;
304  }
305  }
306  return false;
307 }
308 
310  /* These options would cause cross-contamination if values for
311  * mainnet were used while running on regtest/testnet (or vice-versa).
312  * Setting them as section_only_args ensures that sharing a config file
313  * between mainnet and regtest/testnet won't cause problems due to these
314  * parameters by accident. */
315  m_network_only_args{
316  "-addnode", "-connect",
317  "-port", "-bind",
318  "-rpcport", "-rpcbind",
319  "-wallet",
320  }
321 {
322  // nothing to do
323 }
324 
325 const std::set<std::string> ArgsManager::GetUnsuitableSectionOnlyArgs() const
326 {
327  std::set<std::string> unsuitables;
328 
329  LOCK(cs_args);
330 
331  // if there's no section selected, don't worry
332  if (m_network.empty()) return std::set<std::string> {};
333 
334  // if it's okay to use the default section for this network, don't worry
335  if (m_network == CBaseChainParams::MAIN) return std::set<std::string> {};
336 
337  for (const auto& arg : m_network_only_args) {
338  std::pair<bool, std::string> found_result;
339 
340  // if this option is overridden it's fine
341  found_result = ArgsManagerHelper::GetArgHelper(m_override_args, arg);
342  if (found_result.first) continue;
343 
344  // if there's a network-specific value for this option, it's fine
345  found_result = ArgsManagerHelper::GetArgHelper(m_config_args, ArgsManagerHelper::NetworkArg(*this, arg));
346  if (found_result.first) continue;
347 
348  // if there isn't a default value for this option, it's fine
349  found_result = ArgsManagerHelper::GetArgHelper(m_config_args, arg);
350  if (!found_result.first) continue;
351 
352  // otherwise, issue a warning
353  unsuitables.insert(arg);
354  }
355  return unsuitables;
356 }
357 
358 const std::list<SectionInfo> ArgsManager::GetUnrecognizedSections() const
359 {
360  // Section names to be recognized in the config file.
361  static const std::set<std::string> available_sections{
365  };
366 
367  LOCK(cs_args);
368  std::list<SectionInfo> unrecognized = m_config_sections;
369  unrecognized.remove_if([](const SectionInfo& appeared){ return available_sections.find(appeared.m_name) != available_sections.end(); });
370  return unrecognized;
371 }
372 
373 void ArgsManager::SelectConfigNetwork(const std::string& network)
374 {
375  LOCK(cs_args);
376  m_network = network;
377 }
378 
379 bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::string& error)
380 {
381  LOCK(cs_args);
382  m_override_args.clear();
383 
384  for (int i = 1; i < argc; i++) {
385  std::string key(argv[i]);
386  std::string val;
387  size_t is_index = key.find('=');
388  if (is_index != std::string::npos) {
389  val = key.substr(is_index + 1);
390  key.erase(is_index);
391  }
392 #ifdef WIN32
393  std::transform(key.begin(), key.end(), key.begin(), ToLower);
394  if (key[0] == '/')
395  key[0] = '-';
396 #endif
397 
398  if (key[0] != '-')
399  break;
400 
401  // Transform --foo to -foo
402  if (key.length() > 1 && key[1] == '-')
403  key.erase(0, 1);
404 
405  // Check for -nofoo
406  if (InterpretNegatedOption(key, val)) {
407  m_override_args[key].clear();
408  } else {
409  m_override_args[key].push_back(val);
410  }
411 
412  // Check that the arg is known
413  if (!(IsSwitchChar(key[0]) && key.size() == 1)) {
414  if (!IsArgKnown(key)) {
415  error = strprintf("Invalid parameter %s", key.c_str());
416  return false;
417  }
418  }
419  }
420 
421  // we do not allow -includeconf from command line, so we clear it here
422  auto it = m_override_args.find("-includeconf");
423  if (it != m_override_args.end()) {
424  if (it->second.size() > 0) {
425  for (const auto& ic : it->second) {
426  error += "-includeconf cannot be used from commandline; -includeconf=" + ic + "\n";
427  }
428  return false;
429  }
430  }
431  return true;
432 }
433 
434 bool ArgsManager::IsArgKnown(const std::string& key) const
435 {
436  size_t option_index = key.find('.');
437  std::string arg_no_net;
438  if (option_index == std::string::npos) {
439  arg_no_net = key;
440  } else {
441  arg_no_net = std::string("-") + key.substr(option_index + 1, std::string::npos);
442  }
443 
444  LOCK(cs_args);
445  for (const auto& arg_map : m_available_args) {
446  if (arg_map.second.count(arg_no_net)) return true;
447  }
448  return false;
449 }
450 
451 std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const
452 {
453  std::vector<std::string> result = {};
454  if (IsArgNegated(strArg)) return result; // special case
455 
456  LOCK(cs_args);
457 
458  ArgsManagerHelper::AddArgs(result, m_override_args, strArg);
459  if (!m_network.empty()) {
460  ArgsManagerHelper::AddArgs(result, m_config_args, ArgsManagerHelper::NetworkArg(*this, strArg));
461  }
462 
463  if (ArgsManagerHelper::UseDefaultSection(*this, strArg)) {
464  ArgsManagerHelper::AddArgs(result, m_config_args, strArg);
465  }
466 
467  return result;
468 }
469 
470 bool ArgsManager::IsArgSet(const std::string& strArg) const
471 {
472  if (IsArgNegated(strArg)) return true; // special case
473  return ArgsManagerHelper::GetArg(*this, strArg).first;
474 }
475 
476 bool ArgsManager::IsArgNegated(const std::string& strArg) const
477 {
478  LOCK(cs_args);
479 
480  const auto& ov = m_override_args.find(strArg);
481  if (ov != m_override_args.end()) return ov->second.empty();
482 
483  if (!m_network.empty()) {
484  const auto& cfs = m_config_args.find(ArgsManagerHelper::NetworkArg(*this, strArg));
485  if (cfs != m_config_args.end()) return cfs->second.empty();
486  }
487 
488  const auto& cf = m_config_args.find(strArg);
489  if (cf != m_config_args.end()) return cf->second.empty();
490 
491  return false;
492 }
493 
494 std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const
495 {
496  if (IsArgNegated(strArg)) return "0";
497  std::pair<bool,std::string> found_res = ArgsManagerHelper::GetArg(*this, strArg);
498  if (found_res.first) return found_res.second;
499  return strDefault;
500 }
501 
502 int64_t ArgsManager::GetArg(const std::string& strArg, int64_t nDefault) const
503 {
504  if (IsArgNegated(strArg)) return 0;
505  std::pair<bool,std::string> found_res = ArgsManagerHelper::GetArg(*this, strArg);
506  if (found_res.first) return atoi64(found_res.second);
507  return nDefault;
508 }
509 
510 bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const
511 {
512  if (IsArgNegated(strArg)) return false;
513  std::pair<bool,std::string> found_res = ArgsManagerHelper::GetArg(*this, strArg);
514  if (found_res.first) return InterpretBool(found_res.second);
515  return fDefault;
516 }
517 
518 bool ArgsManager::SoftSetArg(const std::string& strArg, const std::string& strValue)
519 {
520  LOCK(cs_args);
521  if (IsArgSet(strArg)) return false;
522  ForceSetArg(strArg, strValue);
523  return true;
524 }
525 
526 bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue)
527 {
528  if (fValue)
529  return SoftSetArg(strArg, std::string("1"));
530  else
531  return SoftSetArg(strArg, std::string("0"));
532 }
533 
534 void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue)
535 {
536  LOCK(cs_args);
537  m_override_args[strArg] = {strValue};
538 }
539 
540 void ArgsManager::AddArg(const std::string& name, const std::string& help, const bool debug_only, const OptionsCategory& cat)
541 {
542  // Split arg name from its help param
543  size_t eq_index = name.find('=');
544  if (eq_index == std::string::npos) {
545  eq_index = name.size();
546  }
547 
548  LOCK(cs_args);
549  std::map<std::string, Arg>& arg_map = m_available_args[cat];
550  auto ret = arg_map.emplace(name.substr(0, eq_index), Arg(name.substr(eq_index, name.size() - eq_index), help, debug_only));
551  assert(ret.second); // Make sure an insertion actually happened
552 }
553 
554 void ArgsManager::AddHiddenArgs(const std::vector<std::string>& names)
555 {
556  for (const std::string& name : names) {
557  AddArg(name, "", false, OptionsCategory::HIDDEN);
558  }
559 }
560 
561 std::string ArgsManager::GetHelpMessage() const
562 {
563  const bool show_debug = gArgs.GetBoolArg("-help-debug", false);
564 
565  std::string usage = "";
566  LOCK(cs_args);
567  for (const auto& arg_map : m_available_args) {
568  switch(arg_map.first) {
570  usage += HelpMessageGroup("Options:");
571  break;
573  usage += HelpMessageGroup("Connection options:");
574  break;
576  usage += HelpMessageGroup("ZeroMQ notification options:");
577  break;
579  usage += HelpMessageGroup("Debugging/Testing options:");
580  break;
582  usage += HelpMessageGroup("Node relay options:");
583  break;
585  usage += HelpMessageGroup("Block creation options:");
586  break;
588  usage += HelpMessageGroup("RPC server options:");
589  break;
591  usage += HelpMessageGroup("Wallet options:");
592  break;
594  if (show_debug) usage += HelpMessageGroup("Wallet debugging/testing options:");
595  break;
597  usage += HelpMessageGroup("Chain selection options:");
598  break;
600  usage += HelpMessageGroup("UI Options:");
601  break;
603  usage += HelpMessageGroup("Commands:");
604  break;
606  usage += HelpMessageGroup("Register Commands:");
607  break;
608  default:
609  break;
610  }
611 
612  // When we get to the hidden options, stop
613  if (arg_map.first == OptionsCategory::HIDDEN) break;
614 
615  for (const auto& arg : arg_map.second) {
616  if (show_debug || !arg.second.m_debug_only) {
617  std::string name;
618  if (arg.second.m_help_param.empty()) {
619  name = arg.first;
620  } else {
621  name = arg.first + arg.second.m_help_param;
622  }
623  usage += HelpMessageOpt(name, arg.second.m_help_text);
624  }
625  }
626  }
627  return usage;
628 }
629 
630 bool HelpRequested(const ArgsManager& args)
631 {
632  return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help") || args.IsArgSet("-help-debug");
633 }
634 
636 {
637  args.AddArg("-?", "Print this help message and exit", false, OptionsCategory::OPTIONS);
638  args.AddHiddenArgs({"-h", "-help"});
639 }
640 
641 static const int screenWidth = 79;
642 static const int optIndent = 2;
643 static const int msgIndent = 7;
644 
645 std::string HelpMessageGroup(const std::string &message) {
646  return std::string(message) + std::string("\n\n");
647 }
648 
649 std::string HelpMessageOpt(const std::string &option, const std::string &message) {
650  return std::string(optIndent,' ') + std::string(option) +
651  std::string("\n") + std::string(msgIndent,' ') +
652  FormatParagraph(message, screenWidth - msgIndent, msgIndent) +
653  std::string("\n\n");
654 }
655 
656 static std::string FormatException(const std::exception* pex, const char* pszThread)
657 {
658 #ifdef WIN32
659  char pszModule[MAX_PATH] = "";
660  GetModuleFileNameA(nullptr, pszModule, sizeof(pszModule));
661 #else
662  const char* pszModule = "bitcoin";
663 #endif
664  if (pex)
665  return strprintf(
666  "EXCEPTION: %s \n%s \n%s in %s \n", typeid(*pex).name(), pex->what(), pszModule, pszThread);
667  else
668  return strprintf(
669  "UNKNOWN EXCEPTION \n%s in %s \n", pszModule, pszThread);
670 }
671 
672 void PrintExceptionContinue(const std::exception* pex, const char* pszThread)
673 {
674  std::string message = FormatException(pex, pszThread);
675  LogPrintf("\n\n************************\n%s\n", message);
676  fprintf(stderr, "\n\n************************\n%s\n", message.c_str());
677 }
678 
680 {
681  // Windows < Vista: C:\Documents and Settings\Username\Application Data\Bitcoin
682  // Windows >= Vista: C:\Users\Username\AppData\Roaming\Bitcoin
683  // Mac: ~/Library/Application Support/Bitcoin
684  // Unix: ~/.bitcoin
685 #ifdef WIN32
686  // Windows
687  return GetSpecialFolderPath(CSIDL_APPDATA) / "Bitcoin";
688 #else
689  fs::path pathRet;
690  char* pszHome = getenv("HOME");
691  if (pszHome == nullptr || strlen(pszHome) == 0)
692  pathRet = fs::path("/");
693  else
694  pathRet = fs::path(pszHome);
695 #ifdef MAC_OSX
696  // Mac
697  return pathRet / "Library/Application Support/Bitcoin";
698 #else
699  // Unix
700  return pathRet / ".bitcoin";
701 #endif
702 #endif
703 }
704 
705 static fs::path g_blocks_path_cache_net_specific;
706 static fs::path pathCached;
707 static fs::path pathCachedNetSpecific;
708 static CCriticalSection csPathCached;
709 
710 const fs::path &GetBlocksDir()
711 {
712 
713  LOCK(csPathCached);
714 
715  fs::path &path = g_blocks_path_cache_net_specific;
716 
717  // This can be called during exceptions by LogPrintf(), so we cache the
718  // value so we don't have to do memory allocations after that.
719  if (!path.empty())
720  return path;
721 
722  if (gArgs.IsArgSet("-blocksdir")) {
723  path = fs::system_complete(gArgs.GetArg("-blocksdir", ""));
724  if (!fs::is_directory(path)) {
725  path = "";
726  return path;
727  }
728  } else {
729  path = GetDataDir(false);
730  }
731 
732  path /= BaseParams().DataDir();
733  path /= "blocks";
734  fs::create_directories(path);
735  return path;
736 }
737 
738 const fs::path &GetDataDir(bool fNetSpecific)
739 {
740 
741  LOCK(csPathCached);
742 
743  fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached;
744 
745  // This can be called during exceptions by LogPrintf(), so we cache the
746  // value so we don't have to do memory allocations after that.
747  if (!path.empty())
748  return path;
749 
750  if (gArgs.IsArgSet("-datadir")) {
751  path = fs::system_complete(gArgs.GetArg("-datadir", ""));
752  if (!fs::is_directory(path)) {
753  path = "";
754  return path;
755  }
756  } else {
757  path = GetDefaultDataDir();
758  }
759  if (fNetSpecific)
760  path /= BaseParams().DataDir();
761 
762  if (fs::create_directories(path)) {
763  // This is the first run, create wallets subdirectory too
764  fs::create_directories(path / "wallets");
765  }
766 
767  return path;
768 }
769 
771 {
772  LOCK(csPathCached);
773 
774  pathCached = fs::path();
775  pathCachedNetSpecific = fs::path();
776  g_blocks_path_cache_net_specific = fs::path();
777 }
778 
779 fs::path GetConfigFile(const std::string& confPath)
780 {
781  return AbsPathForConfigVal(fs::path(confPath), false);
782 }
783 
784 static std::string TrimString(const std::string& str, const std::string& pattern)
785 {
786  std::string::size_type front = str.find_first_not_of(pattern);
787  if (front == std::string::npos) {
788  return std::string();
789  }
790  std::string::size_type end = str.find_last_not_of(pattern);
791  return str.substr(front, end - front + 1);
792 }
793 
794 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)
795 {
796  std::string str, prefix;
797  std::string::size_type pos;
798  int linenr = 1;
799  while (std::getline(stream, str)) {
800  bool used_hash = false;
801  if ((pos = str.find('#')) != std::string::npos) {
802  str = str.substr(0, pos);
803  used_hash = true;
804  }
805  const static std::string pattern = " \t\r\n";
806  str = TrimString(str, pattern);
807  if (!str.empty()) {
808  if (*str.begin() == '[' && *str.rbegin() == ']') {
809  const std::string section = str.substr(1, str.size() - 2);
810  sections.emplace_back(SectionInfo{section, filepath, linenr});
811  prefix = section + '.';
812  } else if (*str.begin() == '-') {
813  error = strprintf("parse error on line %i: %s, options in configuration file must be specified without leading -", linenr, str);
814  return false;
815  } else if ((pos = str.find('=')) != std::string::npos) {
816  std::string name = prefix + TrimString(str.substr(0, pos), pattern);
817  std::string value = TrimString(str.substr(pos + 1), pattern);
818  if (used_hash && name.find("rpcpassword") != std::string::npos) {
819  error = strprintf("parse error on line %i, using # in rpcpassword can be ambiguous and should be avoided", linenr);
820  return false;
821  }
822  options.emplace_back(name, value);
823  if ((pos = name.rfind('.')) != std::string::npos && prefix.length() <= pos) {
824  sections.emplace_back(SectionInfo{name.substr(0, pos), filepath, linenr});
825  }
826  } else {
827  error = strprintf("parse error on line %i: %s", linenr, str);
828  if (str.size() >= 2 && str.substr(0, 2) == "no") {
829  error += strprintf(", if you intended to specify a negated option, use %s=1 instead", str);
830  }
831  return false;
832  }
833  }
834  ++linenr;
835  }
836  return true;
837 }
838 
839 bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys)
840 {
841  LOCK(cs_args);
842  std::vector<std::pair<std::string, std::string>> options;
843  if (!GetConfigOptions(stream, filepath, error, options, m_config_sections)) {
844  return false;
845  }
846  for (const std::pair<std::string, std::string>& option : options) {
847  std::string strKey = std::string("-") + option.first;
848  std::string strValue = option.second;
849 
850  if (InterpretNegatedOption(strKey, strValue)) {
851  m_config_args[strKey].clear();
852  } else {
853  m_config_args[strKey].push_back(strValue);
854  }
855 
856  // Check that the arg is known
857  if (!IsArgKnown(strKey)) {
858  if (!ignore_invalid_keys) {
859  error = strprintf("Invalid configuration value %s", option.first.c_str());
860  return false;
861  } else {
862  LogPrintf("Ignoring unknown configuration value %s\n", option.first);
863  }
864  }
865  }
866  return true;
867 }
868 
869 bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
870 {
871  {
872  LOCK(cs_args);
873  m_config_args.clear();
874  m_config_sections.clear();
875  }
876 
877  const std::string confPath = GetArg("-conf", BITCOIN_CONF_FILENAME);
878  fsbridge::ifstream stream(GetConfigFile(confPath));
879 
880  // ok to not have a config file
881  if (stream.good()) {
882  if (!ReadConfigStream(stream, confPath, error, ignore_invalid_keys)) {
883  return false;
884  }
885  // if there is an -includeconf in the override args, but it is empty, that means the user
886  // passed '-noincludeconf' on the command line, in which case we should not include anything
887  bool emptyIncludeConf;
888  {
889  LOCK(cs_args);
890  emptyIncludeConf = m_override_args.count("-includeconf") == 0;
891  }
892  if (emptyIncludeConf) {
893  std::string chain_id = GetChainName();
894  std::vector<std::string> includeconf(GetArgs("-includeconf"));
895  {
896  // We haven't set m_network yet (that happens in SelectParams()), so manually check
897  // for network.includeconf args.
898  std::vector<std::string> includeconf_net(GetArgs(std::string("-") + chain_id + ".includeconf"));
899  includeconf.insert(includeconf.end(), includeconf_net.begin(), includeconf_net.end());
900  }
901 
902  // Remove -includeconf from configuration, so we can warn about recursion
903  // later
904  {
905  LOCK(cs_args);
906  m_config_args.erase("-includeconf");
907  m_config_args.erase(std::string("-") + chain_id + ".includeconf");
908  }
909 
910  for (const std::string& to_include : includeconf) {
911  fsbridge::ifstream include_config(GetConfigFile(to_include));
912  if (include_config.good()) {
913  if (!ReadConfigStream(include_config, to_include, error, ignore_invalid_keys)) {
914  return false;
915  }
916  LogPrintf("Included configuration file %s\n", to_include.c_str());
917  } else {
918  error = "Failed to include configuration file " + to_include;
919  return false;
920  }
921  }
922 
923  // Warn about recursive -includeconf
924  includeconf = GetArgs("-includeconf");
925  {
926  std::vector<std::string> includeconf_net(GetArgs(std::string("-") + chain_id + ".includeconf"));
927  includeconf.insert(includeconf.end(), includeconf_net.begin(), includeconf_net.end());
928  std::string chain_id_final = GetChainName();
929  if (chain_id_final != chain_id) {
930  // Also warn about recursive includeconf for the chain that was specified in one of the includeconfs
931  includeconf_net = GetArgs(std::string("-") + chain_id_final + ".includeconf");
932  includeconf.insert(includeconf.end(), includeconf_net.begin(), includeconf_net.end());
933  }
934  }
935  for (const std::string& to_include : includeconf) {
936  fprintf(stderr, "warning: -includeconf cannot be used from included files; ignoring -includeconf=%s\n", to_include.c_str());
937  }
938  }
939  }
940 
941  // If datadir is changed in .conf file:
943  if (!fs::is_directory(GetDataDir(false))) {
944  error = strprintf("specified data directory \"%s\" does not exist.", gArgs.GetArg("-datadir", "").c_str());
945  return false;
946  }
947  return true;
948 }
949 
950 std::string ArgsManager::GetChainName() const
951 {
952  LOCK(cs_args);
953  bool fRegTest = ArgsManagerHelper::GetNetBoolArg(*this, "-regtest");
954  bool fTestNet = ArgsManagerHelper::GetNetBoolArg(*this, "-testnet");
955 
956  if (fTestNet && fRegTest)
957  throw std::runtime_error("Invalid combination of -regtest and -testnet.");
958  if (fRegTest)
960  if (fTestNet)
962  return CBaseChainParams::MAIN;
963 }
964 
965 bool RenameOver(fs::path src, fs::path dest)
966 {
967 #ifdef WIN32
968  return MoveFileExW(src.wstring().c_str(), dest.wstring().c_str(),
969  MOVEFILE_REPLACE_EXISTING) != 0;
970 #else
971  int rc = std::rename(src.string().c_str(), dest.string().c_str());
972  return (rc == 0);
973 #endif /* WIN32 */
974 }
975 
981 bool TryCreateDirectories(const fs::path& p)
982 {
983  try
984  {
985  return fs::create_directories(p);
986  } catch (const fs::filesystem_error&) {
987  if (!fs::exists(p) || !fs::is_directory(p))
988  throw;
989  }
990 
991  // create_directories didn't create the directory, it had to have existed already
992  return false;
993 }
994 
995 bool FileCommit(FILE *file)
996 {
997  if (fflush(file) != 0) { // harmless if redundantly called
998  LogPrintf("%s: fflush failed: %d\n", __func__, errno);
999  return false;
1000  }
1001 #ifdef WIN32
1002  HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
1003  if (FlushFileBuffers(hFile) == 0) {
1004  LogPrintf("%s: FlushFileBuffers failed: %d\n", __func__, GetLastError());
1005  return false;
1006  }
1007 #else
1008  #if defined(__linux__) || defined(__NetBSD__)
1009  if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync
1010  LogPrintf("%s: fdatasync failed: %d\n", __func__, errno);
1011  return false;
1012  }
1013  #elif defined(MAC_OSX) && defined(F_FULLFSYNC)
1014  if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success
1015  LogPrintf("%s: fcntl F_FULLFSYNC failed: %d\n", __func__, errno);
1016  return false;
1017  }
1018  #else
1019  if (fsync(fileno(file)) != 0 && errno != EINVAL) {
1020  LogPrintf("%s: fsync failed: %d\n", __func__, errno);
1021  return false;
1022  }
1023  #endif
1024 #endif
1025  return true;
1026 }
1027 
1028 bool TruncateFile(FILE *file, unsigned int length) {
1029 #if defined(WIN32)
1030  return _chsize(_fileno(file), length) == 0;
1031 #else
1032  return ftruncate(fileno(file), length) == 0;
1033 #endif
1034 }
1035 
1040 int RaiseFileDescriptorLimit(int nMinFD) {
1041 #if defined(WIN32)
1042  return 2048;
1043 #else
1044  struct rlimit limitFD;
1045  if (getrlimit(RLIMIT_NOFILE, &limitFD) != -1) {
1046  if (limitFD.rlim_cur < (rlim_t)nMinFD) {
1047  limitFD.rlim_cur = nMinFD;
1048  if (limitFD.rlim_cur > limitFD.rlim_max)
1049  limitFD.rlim_cur = limitFD.rlim_max;
1050  setrlimit(RLIMIT_NOFILE, &limitFD);
1051  getrlimit(RLIMIT_NOFILE, &limitFD);
1052  }
1053  return limitFD.rlim_cur;
1054  }
1055  return nMinFD; // getrlimit failed, assume it's fine
1056 #endif
1057 }
1058 
1063 void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) {
1064 #if defined(WIN32)
1065  // Windows-specific version
1066  HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
1067  LARGE_INTEGER nFileSize;
1068  int64_t nEndPos = (int64_t)offset + length;
1069  nFileSize.u.LowPart = nEndPos & 0xFFFFFFFF;
1070  nFileSize.u.HighPart = nEndPos >> 32;
1071  SetFilePointerEx(hFile, nFileSize, 0, FILE_BEGIN);
1072  SetEndOfFile(hFile);
1073 #elif defined(MAC_OSX)
1074  // OSX specific version
1075  fstore_t fst;
1076  fst.fst_flags = F_ALLOCATECONTIG;
1077  fst.fst_posmode = F_PEOFPOSMODE;
1078  fst.fst_offset = 0;
1079  fst.fst_length = (off_t)offset + length;
1080  fst.fst_bytesalloc = 0;
1081  if (fcntl(fileno(file), F_PREALLOCATE, &fst) == -1) {
1082  fst.fst_flags = F_ALLOCATEALL;
1083  fcntl(fileno(file), F_PREALLOCATE, &fst);
1084  }
1085  ftruncate(fileno(file), fst.fst_length);
1086 #else
1087  #if defined(__linux__)
1088  // Version using posix_fallocate
1089  off_t nEndPos = (off_t)offset + length;
1090  if (0 == posix_fallocate(fileno(file), 0, nEndPos)) return;
1091  #endif
1092  // Fallback version
1093  // TODO: just write one byte per block
1094  static const char buf[65536] = {};
1095  if (fseek(file, offset, SEEK_SET)) {
1096  return;
1097  }
1098  while (length > 0) {
1099  unsigned int now = 65536;
1100  if (length < now)
1101  now = length;
1102  fwrite(buf, 1, now, file); // allowed to fail; this function is advisory anyway
1103  length -= now;
1104  }
1105 #endif
1106 }
1107 
1108 #ifdef WIN32
1109 fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
1110 {
1111  WCHAR pszPath[MAX_PATH] = L"";
1112 
1113  if(SHGetSpecialFolderPathW(nullptr, pszPath, nFolder, fCreate))
1114  {
1115  return fs::path(pszPath);
1116  }
1117 
1118  LogPrintf("SHGetSpecialFolderPathW() failed, could not obtain requested path.\n");
1119  return fs::path("");
1120 }
1121 #endif
1122 
1123 void runCommand(const std::string& strCommand)
1124 {
1125  if (strCommand.empty()) return;
1126 #ifndef WIN32
1127  int nErr = ::system(strCommand.c_str());
1128 #else
1129  int nErr = ::_wsystem(std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().from_bytes(strCommand).c_str());
1130 #endif
1131  if (nErr)
1132  LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
1133 }
1134 
1136 {
1137 #ifdef HAVE_MALLOPT_ARENA_MAX
1138  // glibc-specific: On 32-bit systems set the number of arenas to 1.
1139  // By default, since glibc 2.10, the C library will create up to two heap
1140  // arenas per core. This is known to cause excessive virtual address space
1141  // usage in our usage. Work around it by setting the maximum number of
1142  // arenas to 1.
1143  if (sizeof(void*) == 4) {
1144  mallopt(M_ARENA_MAX, 1);
1145  }
1146 #endif
1147  // On most POSIX systems (e.g. Linux, but not BSD) the environment's locale
1148  // may be invalid, in which case the "C" locale is used as fallback.
1149 #if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
1150  try {
1151  std::locale(""); // Raises a runtime error if current locale is invalid
1152  } catch (const std::runtime_error&) {
1153  setenv("LC_ALL", "C", 1);
1154  }
1155 #elif defined(WIN32)
1156  // Set the default input/output charset is utf-8
1157  SetConsoleCP(CP_UTF8);
1158  SetConsoleOutputCP(CP_UTF8);
1159 #endif
1160  // The path locale is lazy initialized and to avoid deinitialization errors
1161  // in multithreading environments, it is set explicitly by the main thread.
1162  // A dummy locale is used to extract the internal default locale, used by
1163  // fs::path, which is then used to explicitly imbue the path.
1164  std::locale loc = fs::path::imbue(std::locale::classic());
1165 #ifndef WIN32
1166  fs::path::imbue(loc);
1167 #else
1168  fs::path::imbue(std::locale(loc, new std::codecvt_utf8_utf16<wchar_t>()));
1169 #endif
1170 }
1171 
1173 {
1174 #ifdef WIN32
1175  // Initialize Windows Sockets
1176  WSADATA wsadata;
1177  int ret = WSAStartup(MAKEWORD(2,2), &wsadata);
1178  if (ret != NO_ERROR || LOBYTE(wsadata.wVersion ) != 2 || HIBYTE(wsadata.wVersion) != 2)
1179  return false;
1180 #endif
1181  return true;
1182 }
1183 
1185 {
1186  return std::thread::hardware_concurrency();
1187 }
1188 
1189 std::string CopyrightHolders(const std::string& strPrefix)
1190 {
1191  std::string strCopyrightHolders = strPrefix + strprintf(_(COPYRIGHT_HOLDERS), _(COPYRIGHT_HOLDERS_SUBSTITUTION));
1192 
1193  // Check for untranslated substitution to make sure Bitcoin Core copyright is not removed by accident
1194  if (strprintf(COPYRIGHT_HOLDERS, COPYRIGHT_HOLDERS_SUBSTITUTION).find("Bitcoin Core") == std::string::npos) {
1195  strCopyrightHolders += "\n" + strPrefix + "The Bitcoin Core developers";
1196  }
1197  return strCopyrightHolders;
1198 }
1199 
1200 // Obtain the application startup time (used for uptime calculation)
1202 {
1203  return nStartupTime;
1204 }
1205 
1206 fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific)
1207 {
1208  return fs::absolute(path, GetDataDir(net_specific));
1209 }
1210 
1212 {
1213 #ifdef SCHED_BATCH
1214  const static sched_param param{};
1215  if (int ret = pthread_setschedparam(pthread_self(), SCHED_BATCH, &param)) {
1216  LogPrintf("Failed to pthread_setschedparam: %s\n", strerror(errno));
1217  return ret;
1218  }
1219  return 0;
1220 #else
1221  return 1;
1222 #endif
1223 }
1224 
1225 namespace util {
1226 #ifdef WIN32
1227 WinCmdLineArgs::WinCmdLineArgs()
1228 {
1229  wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
1230  std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> utf8_cvt;
1231  argv = new char*[argc];
1232  args.resize(argc);
1233  for (int i = 0; i < argc; i++) {
1234  args[i] = utf8_cvt.to_bytes(wargv[i]);
1235  argv[i] = &*args[i].begin();
1236  }
1237  LocalFree(wargv);
1238 }
1239 
1240 WinCmdLineArgs::~WinCmdLineArgs()
1241 {
1242  delete[] argv;
1243 }
1244 
1245 std::pair<int, char**> WinCmdLineArgs::get()
1246 {
1247  return std::make_pair(argc, argv);
1248 }
1249 #endif
1250 } // namespace util
NODISCARD bool ReadConfigFiles(std::string &error, bool ignore_invalid_keys=false)
Definition: system.cpp:869
#define COPYRIGHT_HOLDERS_SUBSTITUTION
#define COPYRIGHT_HOLDERS
static bool GetNetBoolArg(const ArgsManager &am, const std::string &net_arg) EXCLUSIVE_LOCKS_REQUIRED(am.cs_args)
Definition: system.cpp:250
bool IsArgSet(const std::string &strArg) const
Return true if the given argument has been manually set.
Definition: system.cpp:470
std::string FormatParagraph(const std::string &in, size_t width, size_t indent)
Format a paragraph of text to a fixed width, adding spaces for indentation to any added line...
bool FileCommit(FILE *file)
Definition: system.cpp:995
bool DirIsWritable(const fs::path &directory)
Definition: system.cpp:119
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:15
fs::path GetDefaultDataDir()
Definition: system.cpp:679
bool SoftSetBoolArg(const std::string &strArg, bool fValue)
Set a boolean argument if it doesn&#39;t already have a value.
Definition: system.cpp:526
static const std::string REGTEST
int GetNumCores()
Return the number of cores available on the current system.
Definition: system.cpp:1184
#define strprintf
Definition: tinyformat.h:1066
const char * prefix
Definition: rest.cpp:625
void AddHiddenArgs(const std::vector< std::string > &args)
Add many hidden arguments.
Definition: system.cpp:554
bool TryCreateDirectories(const fs::path &p)
Ignores exceptions thrown by Boost&#39;s create_directories if the requested directory exists...
Definition: system.cpp:981
fs::ifstream ifstream
Definition: fs.h:91
#define MAX_PATH
Definition: compat.h:69
void SetupEnvironment()
Definition: system.cpp:1135
std::string CopyrightHolders(const std::string &strPrefix)
Definition: system.cpp:1189
void SelectConfigNetwork(const std::string &network)
Select the network in use.
Definition: system.cpp:373
const CBaseChainParams & BaseParams()
Return the currently selected parameters.
NODISCARD bool ReadConfigStream(std::istream &stream, const std::string &filepath, std::string &error, bool ignore_invalid_keys=false)
Definition: system.cpp:839
NODISCARD bool ParseParameters(int argc, const char *const argv[], std::string &error)
Definition: system.cpp:379
const fs::path & GetBlocksDir()
Definition: system.cpp:710
int RaiseFileDescriptorLimit(int nMinFD)
this function tries to raise the file descriptor limit to the requested number.
Definition: system.cpp:1040
void ReleaseDirectoryLocks()
Release all directory locks.
Definition: system.cpp:113
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition: system.cpp:510
void ClearDatadirCache()
Definition: system.cpp:770
void ForceSetArg(const std::string &strArg, const std::string &strValue)
Definition: system.cpp:534
bool CheckDiskSpace(const fs::path &dir, uint64_t additional_bytes)
Definition: system.cpp:132
bool IsArgKnown(const std::string &key) const
Check whether we know of this arg.
Definition: system.cpp:434
const char *const BITCOIN_CONF_FILENAME
Definition: system.cpp:70
static bool UseDefaultSection(const ArgsManager &am, const std::string &arg) EXCLUSIVE_LOCKS_REQUIRED(am.cs_args)
Determine whether to use config settings in the default section, See also comments around ArgsManager...
Definition: system.cpp:171
bool SetupNetworking()
Definition: system.cpp:1172
bool SoftSetArg(const std::string &strArg, const std::string &strValue)
Set an argument if it doesn&#39;t already have a value.
Definition: system.cpp:518
std::string m_name
Definition: system.h:134
const std::string & DataDir() const
std::string GetHelpMessage() const
Get the help string.
Definition: system.cpp:561
static const std::string MAIN
BIP70 chain name strings (main, test or regtest)
bool LockDirectory(const fs::path &directory, const std::string lockfile_name, bool probe_only)
Definition: system.cpp:83
bool IsArgNegated(const std::string &strArg) const
Return true if the argument was originally passed as a negated option, i.e.
Definition: system.cpp:476
#define LOCK(cs)
Definition: sync.h:182
const char * name
Definition: rest.cpp:38
void runCommand(const std::string &strCommand)
Definition: system.cpp:1123
int atoi(const std::string &str)
bool HelpRequested(const ArgsManager &args)
Definition: system.cpp:630
const fs::path & GetDataDir(bool fNetSpecific)
Definition: system.cpp:738
static std::string NetworkArg(const ArgsManager &am, const std::string &arg)
Convert regular argument into the network-specific setting.
Definition: system.cpp:177
CCriticalSection cs_args
Definition: system.h:153
UniValue help(const JSONRPCRequest &jsonRequest)
Definition: server.cpp:132
void PrintExceptionContinue(const std::exception *pex, const char *pszThread)
Definition: system.cpp:672
static std::pair< bool, std::string > GetArg(const ArgsManager &am, const std::string &arg)
Definition: system.cpp:214
fs::path AbsPathForConfigVal(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: system.cpp:1206
std::map< std::string, std::vector< std::string > > MapArgs
Definition: system.cpp:167
void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length)
this function tries to make a particular range of a file allocated (corresponding to disk space) it i...
Definition: system.cpp:1063
std::string _(const char *psz)
Translation function.
Definition: system.h:52
bool RenameOver(fs::path src, fs::path dest)
Definition: system.cpp:965
int64_t atoi64(const char *psz)
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:51
Internal helper functions for ArgsManager.
Definition: system.cpp:165
int64_t GetStartupTime()
Server/client environment: argument handling, config file parsing, thread wrappers, startup time.
Definition: system.cpp:1201
bool TruncateFile(FILE *file, unsigned int length)
Definition: system.cpp:1028
fs::path GetConfigFile(const std::string &confPath)
Definition: system.cpp:779
void UnlockDirectory(const fs::path &directory, const std::string &lockfile_name)
Definition: system.cpp:107
void AddArg(const std::string &name, const std::string &help, const bool debug_only, const OptionsCategory &cat)
Add argument.
Definition: system.cpp:540
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: system.cpp:494
ArgsManager gArgs
Definition: system.cpp:72
int ScheduleBatchPriority()
On platforms that support it, tell the kernel the calling thread is CPU-intensive and non-interactive...
Definition: system.cpp:1211
void SetupHelpOptions(ArgsManager &args)
Add help options to the args manager.
Definition: system.cpp:635
static const std::string TESTNET
std::string GetChainName() const
Looks for -regtest, -testnet and returns the appropriate BIP70 chain name.
Definition: system.cpp:950
const std::list< SectionInfo > GetUnrecognizedSections() const
Log warnings for unrecognized section names in the config file.
Definition: system.cpp:358
bool IsSwitchChar(char c)
Definition: system.h:105
constexpr char ToLower(char c)
Converts the given character to its lowercase equivalent.
Definition: strencodings.h:206
OptionsCategory
Definition: system.h:114
const int64_t nStartupTime
Definition: system.cpp:68
static void AddArgs(std::vector< std::string > &res, const MapArgs &map_args, const std::string &arg)
Find arguments in a map and add them to a vector.
Definition: system.cpp:184
std::vector< std::string > GetArgs(const std::string &strArg) const
Return a vector of strings of the given argument.
Definition: system.cpp:451
std::string HelpMessageOpt(const std::string &option, const std::string &message)
Format a string to be used as option description in help messages.
Definition: system.cpp:649
int64_t GetTime()
Return system time (or mocked time, if set)
Definition: time.cpp:20
std::string HelpMessageGroup(const std::string &message)
Format a string to be used as group of options in help messages.
Definition: system.cpp:645
const std::set< std::string > GetUnsuitableSectionOnlyArgs() const
Log warnings for options in m_section_only_args when they are specified in the default section but no...
Definition: system.cpp:325
bool error(const char *fmt, const Args &... args)
Definition: system.h:61
static std::pair< bool, std::string > GetArgHelper(const MapArgs &map_args, const std::string &arg, bool getLast=false)
Return true/false if an argument is set in a map, and also return the first (or last) of the possibly...
Definition: system.cpp:195