1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2020 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 #include <util/string.h>
11 #include <util/translation.h>
12 #include <regex>
13 #include <iomanip>
14
15
16 #if (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__))
17 #include <pthread.h>
18 #include <pthread_np.h>
19 #endif
20
21 #ifndef WIN32
22 // for posix_fallocate
23 #ifdef __linux__
24
25 #ifdef _POSIX_C_SOURCE
26 #undef _POSIX_C_SOURCE
27 #endif
28
29 #define _POSIX_C_SOURCE 200112L
30
31 #endif // __linux__
32
33 #include <algorithm>
34 #include <fcntl.h>
35 #include <sched.h>
36 #include <sys/resource.h>
37 #include <sys/stat.h>
38
39 #else
40
41 #ifdef _MSC_VER
42 #pragma warning(disable:4786)
43 #pragma warning(disable:4804)
44 #pragma warning(disable:4805)
45 #pragma warning(disable:4717)
46 #endif
47
48 #ifdef _WIN32_IE
49 #undef _WIN32_IE
50 #endif
51 #define _WIN32_IE 0x0501
52
53 #define WIN32_LEAN_AND_MEAN 1
54 #ifndef NOMINMAX
55 #define NOMINMAX
56 #endif
57 #include <codecvt>
58
59 #include <io.h> /* for _commit */
60 #include <shellapi.h>
61 #include <shlobj.h>
62 #endif
63
64 #ifdef HAVE_MALLOPT_ARENA_MAX
65 #include <malloc.h>
66 #endif
67
68 #include <boost/algorithm/string/replace.hpp>
69 #include <thread>
70 #include <typeinfo>
71 #include <univalue.h>
72
73 // Application startup time (used for uptime calculation)
74 const int64_t nStartupTime = GetTime();
75
76 const char * const BITCOIN_CONF_FILENAME = "qtum.conf";
77
78 ArgsManager gArgs;
79
80 /** A map that contains all the currently held directory locks. After
81 * successful locking, these will be held here until the global destructor
82 * cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks
83 * is called.
84 */
85 static std::map<std::string, std::unique_ptr<fsbridge::FileLock>> dir_locks;
86 /** Mutex to protect dir_locks. */
87 static std::mutex cs_dir_locks;
88
LockDirectory(const fs::path & directory,const std::string lockfile_name,bool probe_only,bool try_lock)89 bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only, bool try_lock)
90 {
91 std::lock_guard<std::mutex> ulock(cs_dir_locks);
92 fs::path pathLockFile = directory / lockfile_name;
93
94 // If a lock for this directory already exists in the map, don't try to re-lock it
95 if (dir_locks.count(pathLockFile.string())) {
96 return true;
97 }
98
99 // Create empty lock file if it doesn't exist.
100 FILE* file = fsbridge::fopen(pathLockFile, "a");
101 if (file) fclose(file);
102 auto lock = MakeUnique<fsbridge::FileLock>(pathLockFile);
103 if (try_lock && !lock->TryLock()) {
104 return error("Error while attempting to lock directory %s: %s", directory.string(), lock->GetReason());
105 }
106 if (!probe_only) {
107 // Lock successful and we're not just probing, put it into the map
108 dir_locks.emplace(pathLockFile.string(), std::move(lock));
109 }
110 return true;
111 }
112
UnlockDirectory(const fs::path & directory,const std::string & lockfile_name)113 void UnlockDirectory(const fs::path& directory, const std::string& lockfile_name)
114 {
115 std::lock_guard<std::mutex> lock(cs_dir_locks);
116 dir_locks.erase((directory / lockfile_name).string());
117 }
118
ReleaseDirectoryLocks()119 void ReleaseDirectoryLocks()
120 {
121 std::lock_guard<std::mutex> ulock(cs_dir_locks);
122 dir_locks.clear();
123 }
124
DirIsWritable(const fs::path & directory)125 bool DirIsWritable(const fs::path& directory)
126 {
127 fs::path tmpFile = directory / fs::unique_path();
128
129 FILE* file = fsbridge::fopen(tmpFile, "a");
130 if (!file) return false;
131
132 fclose(file);
133 remove(tmpFile);
134
135 return true;
136 }
137
CheckDiskSpace(const fs::path & dir,uint64_t additional_bytes)138 bool CheckDiskSpace(const fs::path& dir, uint64_t additional_bytes)
139 {
140 constexpr uint64_t min_disk_space = 52428800; // 50 MiB
141
142 uint64_t free_bytes_available = fs::space(dir).available;
143 return free_bytes_available >= min_disk_space + additional_bytes;
144 }
145
146 /**
147 * Interpret a string argument as a boolean.
148 *
149 * The definition of atoi() requires that non-numeric string values like "foo",
150 * return 0. This means that if a user unintentionally supplies a non-integer
151 * argument here, the return value is always false. This means that -foo=false
152 * does what the user probably expects, but -foo=true is well defined but does
153 * not do what they probably expected.
154 *
155 * The return value of atoi() is undefined when given input not representable as
156 * an int. On most systems this means string value between "-2147483648" and
157 * "2147483647" are well defined (this method will return true). Setting
158 * -txindex=2147483648 on most systems, however, is probably undefined.
159 *
160 * For a more extensive discussion of this topic (and a wide range of opinions
161 * on the Right Way to change this code), see PR12713.
162 */
InterpretBool(const std::string & strValue)163 static bool InterpretBool(const std::string& strValue)
164 {
165 if (strValue.empty())
166 return true;
167 return (atoi(strValue) != 0);
168 }
169
SettingName(const std::string & arg)170 static std::string SettingName(const std::string& arg)
171 {
172 return arg.size() > 0 && arg[0] == '-' ? arg.substr(1) : arg;
173 }
174
175 /**
176 * Interpret -nofoo as if the user supplied -foo=0.
177 *
178 * This method also tracks when the -no form was supplied, and if so,
179 * checks whether there was a double-negative (-nofoo=0 -> -foo=1).
180 *
181 * If there was not a double negative, it removes the "no" from the key
182 * and returns false.
183 *
184 * If there was a double negative, it removes "no" from the key, and
185 * returns true.
186 *
187 * If there was no "no", it returns the string value untouched.
188 *
189 * Where an option was negated can be later checked using the
190 * IsArgNegated() method. One use case for this is to have a way to disable
191 * options that are not normally boolean (e.g. using -nodebuglogfile to request
192 * that debug log output is not sent to any file at all).
193 */
194
InterpretOption(std::string & section,std::string & key,const std::string & value)195 static util::SettingsValue InterpretOption(std::string& section, std::string& key, const std::string& value)
196 {
197 // Split section name from key name for keys like "testnet.foo" or "regtest.bar"
198 size_t option_index = key.find('.');
199 if (option_index != std::string::npos) {
200 section = key.substr(0, option_index);
201 key.erase(0, option_index + 1);
202 }
203 if (key.substr(0, 2) == "no") {
204 key.erase(0, 2);
205 // Double negatives like -nofoo=0 are supported (but discouraged)
206 if (!InterpretBool(value)) {
207 LogPrintf("Warning: parsed potentially confusing double-negative -%s=%s\n", key, value);
208 return true;
209 }
210 return false;
211 }
212 return value;
213 }
214
215 /**
216 * Check settings value validity according to flags.
217 *
218 * TODO: Add more meaningful error checks here in the future
219 * See "here's how the flags are meant to behave" in
220 * https://github.com/bitcoin/bitcoin/pull/16097#issuecomment-514627823
221 */
CheckValid(const std::string & key,const util::SettingsValue & val,unsigned int flags,std::string & error)222 static bool CheckValid(const std::string& key, const util::SettingsValue& val, unsigned int flags, std::string& error)
223 {
224 if (val.isBool() && !(flags & ArgsManager::ALLOW_BOOL)) {
225 error = strprintf("Negating of -%s is meaningless and therefore forbidden", key);
226 return false;
227 }
228 return true;
229 }
230
ArgsManager()231 ArgsManager::ArgsManager()
232 {
233 // nothing to do
234 }
235
GetUnsuitableSectionOnlyArgs() const236 const std::set<std::string> ArgsManager::GetUnsuitableSectionOnlyArgs() const
237 {
238 std::set<std::string> unsuitables;
239
240 LOCK(cs_args);
241
242 // if there's no section selected, don't worry
243 if (m_network.empty()) return std::set<std::string> {};
244
245 // if it's okay to use the default section for this network, don't worry
246 if (m_network == CBaseChainParams::MAIN) return std::set<std::string> {};
247
248 for (const auto& arg : m_network_only_args) {
249 if (OnlyHasDefaultSectionSetting(m_settings, m_network, SettingName(arg))) {
250 unsuitables.insert(arg);
251 }
252 }
253 return unsuitables;
254 }
255
GetUnrecognizedSections() const256 const std::list<SectionInfo> ArgsManager::GetUnrecognizedSections() const
257 {
258 // Section names to be recognized in the config file.
259 static const std::set<std::string> available_sections{
260 CBaseChainParams::REGTEST,
261 CBaseChainParams::TESTNET,
262 CBaseChainParams::MAIN
263 };
264
265 LOCK(cs_args);
266 std::list<SectionInfo> unrecognized = m_config_sections;
267 unrecognized.remove_if([](const SectionInfo& appeared){ return available_sections.find(appeared.m_name) != available_sections.end(); });
268 return unrecognized;
269 }
270
SelectConfigNetwork(const std::string & network)271 void ArgsManager::SelectConfigNetwork(const std::string& network)
272 {
273 LOCK(cs_args);
274 m_network = network;
275 }
276
ParseParameters(int argc,const char * const argv[],std::string & error)277 bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::string& error)
278 {
279 LOCK(cs_args);
280 m_settings.command_line_options.clear();
281
282 for (int i = 1; i < argc; i++) {
283 std::string key(argv[i]);
284
285 #ifdef MAC_OSX
286 // At the first time when a user gets the "App downloaded from the
287 // internet" warning, and clicks the Open button, macOS passes
288 // a unique process serial number (PSN) as -psn_... command-line
289 // argument, which we filter out.
290 if (key.substr(0, 5) == "-psn_") continue;
291 #endif
292
293 if (key == "-") break; //bitcoin-tx using stdin
294 std::string val;
295 size_t is_index = key.find('=');
296 if (is_index != std::string::npos) {
297 val = key.substr(is_index + 1);
298 key.erase(is_index);
299 }
300 #ifdef WIN32
301 key = ToLower(key);
302 if (key[0] == '/')
303 key[0] = '-';
304 #endif
305
306 if (key[0] != '-')
307 break;
308
309 // Transform --foo to -foo
310 if (key.length() > 1 && key[1] == '-')
311 key.erase(0, 1);
312
313 // Transform -foo to foo
314 key.erase(0, 1);
315 std::string section;
316 util::SettingsValue value = InterpretOption(section, key, val);
317 Optional<unsigned int> flags = GetArgFlags('-' + key);
318
319 // Unknown command line options and command line options with dot
320 // characters (which are returned from InterpretOption with nonempty
321 // section strings) are not valid.
322 if (!flags || !section.empty()) {
323 error = strprintf("Invalid parameter %s", argv[i]);
324 return false;
325 }
326
327 if (!CheckValid(key, value, *flags, error)) return false;
328
329 m_settings.command_line_options[key].push_back(value);
330 }
331
332 // we do not allow -includeconf from command line
333 bool success = true;
334 if (auto* includes = util::FindKey(m_settings.command_line_options, "includeconf")) {
335 for (const auto& include : util::SettingsSpan(*includes)) {
336 error += "-includeconf cannot be used from commandline; -includeconf=" + include.get_str() + "\n";
337 success = false;
338 }
339 }
340 return success;
341 }
342
GetArgFlags(const std::string & name) const343 Optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) const
344 {
345 LOCK(cs_args);
346 for (const auto& arg_map : m_available_args) {
347 const auto search = arg_map.second.find(name);
348 if (search != arg_map.second.end()) {
349 return search->second.m_flags;
350 }
351 }
352 return nullopt;
353 }
354
GetArgs(const std::string & strArg) const355 std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const
356 {
357 std::vector<std::string> result;
358 for (const util::SettingsValue& value : GetSettingsList(strArg)) {
359 result.push_back(value.isFalse() ? "0" : value.isTrue() ? "1" : value.get_str());
360 }
361 return result;
362 }
363
IsArgSet(const std::string & strArg) const364 bool ArgsManager::IsArgSet(const std::string& strArg) const
365 {
366 return !GetSetting(strArg).isNull();
367 }
368
IsArgNegated(const std::string & strArg) const369 bool ArgsManager::IsArgNegated(const std::string& strArg) const
370 {
371 return GetSetting(strArg).isFalse();
372 }
373
GetArg(const std::string & strArg,const std::string & strDefault) const374 std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const
375 {
376 const util::SettingsValue value = GetSetting(strArg);
377 return value.isNull() ? strDefault : value.isFalse() ? "0" : value.isTrue() ? "1" : value.get_str();
378 }
379
GetArg(const std::string & strArg,int64_t nDefault) const380 int64_t ArgsManager::GetArg(const std::string& strArg, int64_t nDefault) const
381 {
382 const util::SettingsValue value = GetSetting(strArg);
383 return value.isNull() ? nDefault : value.isFalse() ? 0 : value.isTrue() ? 1 : value.isNum() ? value.get_int64() : atoi64(value.get_str());
384 }
385
GetBoolArg(const std::string & strArg,bool fDefault) const386 bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const
387 {
388 const util::SettingsValue value = GetSetting(strArg);
389 return value.isNull() ? fDefault : value.isBool() ? value.get_bool() : InterpretBool(value.get_str());
390 }
391
SoftSetArg(const std::string & strArg,const std::string & strValue)392 bool ArgsManager::SoftSetArg(const std::string& strArg, const std::string& strValue)
393 {
394 LOCK(cs_args);
395 if (IsArgSet(strArg)) return false;
396 ForceSetArg(strArg, strValue);
397 return true;
398 }
399
SoftSetBoolArg(const std::string & strArg,bool fValue)400 bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue)
401 {
402 if (fValue)
403 return SoftSetArg(strArg, std::string("1"));
404 else
405 return SoftSetArg(strArg, std::string("0"));
406 }
407
ForceSetArg(const std::string & strArg,const std::string & strValue)408 void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue)
409 {
410 LOCK(cs_args);
411 m_settings.forced_settings[SettingName(strArg)] = strValue;
412 }
413
AddArg(const std::string & name,const std::string & help,unsigned int flags,const OptionsCategory & cat)414 void ArgsManager::AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat)
415 {
416 // Split arg name from its help param
417 size_t eq_index = name.find('=');
418 if (eq_index == std::string::npos) {
419 eq_index = name.size();
420 }
421 std::string arg_name = name.substr(0, eq_index);
422
423 LOCK(cs_args);
424 std::map<std::string, Arg>& arg_map = m_available_args[cat];
425 auto ret = arg_map.emplace(arg_name, Arg{name.substr(eq_index, name.size() - eq_index), help, flags});
426 assert(ret.second); // Make sure an insertion actually happened
427
428 if (flags & ArgsManager::NETWORK_ONLY) {
429 m_network_only_args.emplace(arg_name);
430 }
431 }
432
AddHiddenArgs(const std::vector<std::string> & names)433 void ArgsManager::AddHiddenArgs(const std::vector<std::string>& names)
434 {
435 for (const std::string& name : names) {
436 AddArg(name, "", ArgsManager::ALLOW_ANY, OptionsCategory::HIDDEN);
437 }
438 }
439
GetHelpMessage() const440 std::string ArgsManager::GetHelpMessage() const
441 {
442 const bool show_debug = gArgs.GetBoolArg("-help-debug", false);
443
444 std::string usage = "";
445 LOCK(cs_args);
446 for (const auto& arg_map : m_available_args) {
447 switch(arg_map.first) {
448 case OptionsCategory::OPTIONS:
449 usage += HelpMessageGroup("Options:");
450 break;
451 case OptionsCategory::CONNECTION:
452 usage += HelpMessageGroup("Connection options:");
453 break;
454 case OptionsCategory::ZMQ:
455 usage += HelpMessageGroup("ZeroMQ notification options:");
456 break;
457 case OptionsCategory::DEBUG_TEST:
458 usage += HelpMessageGroup("Debugging/Testing options:");
459 break;
460 case OptionsCategory::NODE_RELAY:
461 usage += HelpMessageGroup("Node relay options:");
462 break;
463 case OptionsCategory::BLOCK_CREATION:
464 usage += HelpMessageGroup("Block creation options:");
465 break;
466 case OptionsCategory::RPC:
467 usage += HelpMessageGroup("RPC server options:");
468 break;
469 case OptionsCategory::WALLET:
470 usage += HelpMessageGroup("Wallet options:");
471 break;
472 case OptionsCategory::WALLET_DEBUG_TEST:
473 if (show_debug) usage += HelpMessageGroup("Wallet debugging/testing options:");
474 break;
475 case OptionsCategory::CHAINPARAMS:
476 usage += HelpMessageGroup("Chain selection options:");
477 break;
478 case OptionsCategory::GUI:
479 usage += HelpMessageGroup("UI Options:");
480 break;
481 case OptionsCategory::COMMANDS:
482 usage += HelpMessageGroup("Commands:");
483 break;
484 case OptionsCategory::REGISTER_COMMANDS:
485 usage += HelpMessageGroup("Register Commands:");
486 break;
487 default:
488 break;
489 }
490
491 // When we get to the hidden options, stop
492 if (arg_map.first == OptionsCategory::HIDDEN) break;
493
494 for (const auto& arg : arg_map.second) {
495 if (show_debug || !(arg.second.m_flags & ArgsManager::DEBUG_ONLY)) {
496 std::string name;
497 if (arg.second.m_help_param.empty()) {
498 name = arg.first;
499 } else {
500 name = arg.first + arg.second.m_help_param;
501 }
502 usage += HelpMessageOpt(name, arg.second.m_help_text);
503 }
504 }
505 }
506 return usage;
507 }
508
HelpRequested(const ArgsManager & args)509 bool HelpRequested(const ArgsManager& args)
510 {
511 return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help") || args.IsArgSet("-help-debug");
512 }
513
SetupHelpOptions(ArgsManager & args)514 void SetupHelpOptions(ArgsManager& args)
515 {
516 args.AddArg("-?", "Print this help message and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
517 args.AddHiddenArgs({"-h", "-help"});
518 }
519
520 static const int screenWidth = 79;
521 static const int optIndent = 2;
522 static const int msgIndent = 7;
523
HelpMessageGroup(const std::string & message)524 std::string HelpMessageGroup(const std::string &message) {
525 return std::string(message) + std::string("\n\n");
526 }
527
HelpMessageOpt(const std::string & option,const std::string & message)528 std::string HelpMessageOpt(const std::string &option, const std::string &message) {
529 return std::string(optIndent,' ') + std::string(option) +
530 std::string("\n") + std::string(msgIndent,' ') +
531 FormatParagraph(message, screenWidth - msgIndent, msgIndent) +
532 std::string("\n\n");
533 }
534
FormatException(const std::exception * pex,const char * pszThread)535 static std::string FormatException(const std::exception* pex, const char* pszThread)
536 {
537 #ifdef WIN32
538 char pszModule[MAX_PATH] = "";
539 GetModuleFileNameA(nullptr, pszModule, sizeof(pszModule));
540 #else
541 const char* pszModule = "bitcoin";
542 #endif
543 if (pex)
544 return strprintf(
545 "EXCEPTION: %s \n%s \n%s in %s \n", typeid(*pex).name(), pex->what(), pszModule, pszThread);
546 else
547 return strprintf(
548 "UNKNOWN EXCEPTION \n%s in %s \n", pszModule, pszThread);
549 }
550
PrintExceptionContinue(const std::exception * pex,const char * pszThread)551 void PrintExceptionContinue(const std::exception* pex, const char* pszThread)
552 {
553 std::string message = FormatException(pex, pszThread);
554 LogPrintf("\n\n************************\n%s\n", message);
555 tfm::format(std::cerr, "\n\n************************\n%s\n", message);
556 }
557
GetDefaultDataDir()558 fs::path GetDefaultDataDir()
559 {
560 // Windows < Vista: C:\Documents and Settings\Username\Application Data\Qtum
561 // Windows >= Vista: C:\Users\Username\AppData\Roaming\Qtum
562 // Mac: ~/Library/Application Support/Qtum
563 // Unix: ~/.qtum
564 #ifdef WIN32
565 // Windows
566 return GetSpecialFolderPath(CSIDL_APPDATA) / "Qtum";
567 #else
568 fs::path pathRet;
569 char* pszHome = getenv("HOME");
570 if (pszHome == nullptr || strlen(pszHome) == 0)
571 pathRet = fs::path("/");
572 else
573 pathRet = fs::path(pszHome);
574 #ifdef MAC_OSX
575 // Mac
576 return pathRet / "Library/Application Support/Qtum";
577 #else
578 // Unix
579 return pathRet / ".qtum";
580 #endif
581 #endif
582 }
583
584 static fs::path g_blocks_path_cache_net_specific;
585 static fs::path pathCached;
586 static fs::path pathCachedNetSpecific;
587 static RecursiveMutex csPathCached;
588
GetBlocksDir()589 const fs::path &GetBlocksDir()
590 {
591 LOCK(csPathCached);
592 fs::path &path = g_blocks_path_cache_net_specific;
593
594 // Cache the path to avoid calling fs::create_directories on every call of
595 // this function
596 if (!path.empty()) return path;
597
598 if (gArgs.IsArgSet("-blocksdir")) {
599 path = fs::system_complete(gArgs.GetArg("-blocksdir", ""));
600 if (!fs::is_directory(path)) {
601 path = "";
602 return path;
603 }
604 } else {
605 path = GetDataDir(false);
606 }
607
608 path /= BaseParams().DataDir();
609 path /= "blocks";
610 fs::create_directories(path);
611 return path;
612 }
613
GetDataDir(bool fNetSpecific)614 const fs::path &GetDataDir(bool fNetSpecific)
615 {
616 LOCK(csPathCached);
617 fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached;
618
619 // Cache the path to avoid calling fs::create_directories on every call of
620 // this function
621 if (!path.empty()) return path;
622
623 std::string datadir = gArgs.GetArg("-datadir", "");
624 if (!datadir.empty()) {
625 path = fs::system_complete(datadir);
626 if (!fs::is_directory(path)) {
627 path = "";
628 return path;
629 }
630 } else {
631 path = GetDefaultDataDir();
632 }
633 if (fNetSpecific)
634 path /= BaseParams().DataDir();
635
636 if (fs::create_directories(path)) {
637 // This is the first run, create wallets subdirectory too
638 fs::create_directories(path / "wallets");
639 }
640
641 return path;
642 }
643
CheckDataDirOption()644 bool CheckDataDirOption()
645 {
646 std::string datadir = gArgs.GetArg("-datadir", "");
647 return datadir.empty() || fs::is_directory(fs::system_complete(datadir));
648 }
649
ClearDatadirCache()650 void ClearDatadirCache()
651 {
652 LOCK(csPathCached);
653
654 pathCached = fs::path();
655 pathCachedNetSpecific = fs::path();
656 g_blocks_path_cache_net_specific = fs::path();
657 }
658
GetConfigFile(const std::string & confPath)659 fs::path GetConfigFile(const std::string& confPath)
660 {
661 return AbsPathForConfigVal(fs::path(confPath), false);
662 }
663
GetConfigOptions(std::istream & stream,const std::string & filepath,std::string & error,std::vector<std::pair<std::string,std::string>> & options,std::list<SectionInfo> & sections)664 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)
665 {
666 std::string str, prefix;
667 std::string::size_type pos;
668 int linenr = 1;
669 while (std::getline(stream, str)) {
670 bool used_hash = false;
671 if ((pos = str.find('#')) != std::string::npos) {
672 str = str.substr(0, pos);
673 used_hash = true;
674 }
675 const static std::string pattern = " \t\r\n";
676 str = TrimString(str, pattern);
677 if (!str.empty()) {
678 if (*str.begin() == '[' && *str.rbegin() == ']') {
679 const std::string section = str.substr(1, str.size() - 2);
680 sections.emplace_back(SectionInfo{section, filepath, linenr});
681 prefix = section + '.';
682 } else if (*str.begin() == '-') {
683 error = strprintf("parse error on line %i: %s, options in configuration file must be specified without leading -", linenr, str);
684 return false;
685 } else if ((pos = str.find('=')) != std::string::npos) {
686 std::string name = prefix + TrimString(str.substr(0, pos), pattern);
687 std::string value = TrimString(str.substr(pos + 1), pattern);
688 if (used_hash && name.find("rpcpassword") != std::string::npos) {
689 error = strprintf("parse error on line %i, using # in rpcpassword can be ambiguous and should be avoided", linenr);
690 return false;
691 }
692 options.emplace_back(name, value);
693 if ((pos = name.rfind('.')) != std::string::npos && prefix.length() <= pos) {
694 sections.emplace_back(SectionInfo{name.substr(0, pos), filepath, linenr});
695 }
696 } else {
697 error = strprintf("parse error on line %i: %s", linenr, str);
698 if (str.size() >= 2 && str.substr(0, 2) == "no") {
699 error += strprintf(", if you intended to specify a negated option, use %s=1 instead", str);
700 }
701 return false;
702 }
703 }
704 ++linenr;
705 }
706 return true;
707 }
708
ReadConfigStream(std::istream & stream,const std::string & filepath,std::string & error,bool ignore_invalid_keys)709 bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys)
710 {
711 LOCK(cs_args);
712 std::vector<std::pair<std::string, std::string>> options;
713 if (!GetConfigOptions(stream, filepath, error, options, m_config_sections)) {
714 return false;
715 }
716 for (const std::pair<std::string, std::string>& option : options) {
717 std::string section;
718 std::string key = option.first;
719 util::SettingsValue value = InterpretOption(section, key, option.second);
720 Optional<unsigned int> flags = GetArgFlags('-' + key);
721 if (flags) {
722 if (!CheckValid(key, value, *flags, error)) {
723 return false;
724 }
725 m_settings.ro_config[section][key].push_back(value);
726 } else {
727 if (ignore_invalid_keys) {
728 LogPrintf("Ignoring unknown configuration value %s\n", option.first);
729 } else {
730 error = strprintf("Invalid configuration value %s", option.first);
731 return false;
732 }
733 }
734 }
735 return true;
736 }
737
ReadConfigFiles(std::string & error,bool ignore_invalid_keys)738 bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
739 {
740 {
741 LOCK(cs_args);
742 m_settings.ro_config.clear();
743 m_config_sections.clear();
744 }
745
746 const std::string confPath = GetArg("-conf", BITCOIN_CONF_FILENAME);
747 fsbridge::ifstream stream(GetConfigFile(confPath));
748
749 // ok to not have a config file
750 if (stream.good()) {
751 if (!ReadConfigStream(stream, confPath, error, ignore_invalid_keys)) {
752 return false;
753 }
754 // `-includeconf` cannot be included in the command line arguments except
755 // as `-noincludeconf` (which indicates that no included conf file should be used).
756 bool use_conf_file{true};
757 {
758 LOCK(cs_args);
759 if (auto* includes = util::FindKey(m_settings.command_line_options, "includeconf")) {
760 // ParseParameters() fails if a non-negated -includeconf is passed on the command-line
761 assert(util::SettingsSpan(*includes).last_negated());
762 use_conf_file = false;
763 }
764 }
765 if (use_conf_file) {
766 std::string chain_id = GetChainName();
767 std::vector<std::string> conf_file_names;
768
769 auto add_includes = [&](const std::string& network, size_t skip = 0) {
770 size_t num_values = 0;
771 LOCK(cs_args);
772 if (auto* section = util::FindKey(m_settings.ro_config, network)) {
773 if (auto* values = util::FindKey(*section, "includeconf")) {
774 for (size_t i = std::max(skip, util::SettingsSpan(*values).negated()); i < values->size(); ++i) {
775 conf_file_names.push_back((*values)[i].get_str());
776 }
777 num_values = values->size();
778 }
779 }
780 return num_values;
781 };
782
783 // We haven't set m_network yet (that happens in SelectParams()), so manually check
784 // for network.includeconf args.
785 const size_t chain_includes = add_includes(chain_id);
786 const size_t default_includes = add_includes({});
787
788 for (const std::string& conf_file_name : conf_file_names) {
789 fsbridge::ifstream conf_file_stream(GetConfigFile(conf_file_name));
790 if (conf_file_stream.good()) {
791 if (!ReadConfigStream(conf_file_stream, conf_file_name, error, ignore_invalid_keys)) {
792 return false;
793 }
794 LogPrintf("Included configuration file %s\n", conf_file_name);
795 } else {
796 error = "Failed to include configuration file " + conf_file_name;
797 return false;
798 }
799 }
800
801 // Warn about recursive -includeconf
802 conf_file_names.clear();
803 add_includes(chain_id, /* skip= */ chain_includes);
804 add_includes({}, /* skip= */ default_includes);
805 std::string chain_id_final = GetChainName();
806 if (chain_id_final != chain_id) {
807 // Also warn about recursive includeconf for the chain that was specified in one of the includeconfs
808 add_includes(chain_id_final);
809 }
810 for (const std::string& conf_file_name : conf_file_names) {
811 tfm::format(std::cerr, "warning: -includeconf cannot be used from included files; ignoring -includeconf=%s\n", conf_file_name);
812 }
813 }
814 }
815
816 // If datadir is changed in .conf file:
817 ClearDatadirCache();
818 if (!CheckDataDirOption()) {
819 error = strprintf("specified data directory \"%s\" does not exist.", gArgs.GetArg("-datadir", ""));
820 return false;
821 }
822 return true;
823 }
824
GetChainName() const825 std::string ArgsManager::GetChainName() const
826 {
827 auto get_net = [&](const std::string& arg) {
828 LOCK(cs_args);
829 util::SettingsValue value = util::GetSetting(m_settings, /* section= */ "", SettingName(arg),
830 /* ignore_default_section_config= */ false,
831 /* get_chain_name= */ true);
832 return value.isNull() ? false : value.isBool() ? value.get_bool() : InterpretBool(value.get_str());
833 };
834
835 const bool fRegTest = get_net("-regtest");
836 const bool fTestNet = get_net("-testnet");
837 const bool is_chain_arg_set = IsArgSet("-chain");
838
839 if ((int)is_chain_arg_set + (int)fRegTest + (int)fTestNet > 1) {
840 throw std::runtime_error("Invalid combination of -regtest, -testnet and -chain. Can use at most one.");
841 }
842 if (fRegTest)
843 return CBaseChainParams::REGTEST;
844 if (fTestNet)
845 return CBaseChainParams::TESTNET;
846 return GetArg("-chain", CBaseChainParams::MAIN);
847 }
848
UseDefaultSection(const std::string & arg) const849 bool ArgsManager::UseDefaultSection(const std::string& arg) const
850 {
851 return m_network == CBaseChainParams::MAIN || m_network_only_args.count(arg) == 0;
852 }
853
GetSetting(const std::string & arg) const854 util::SettingsValue ArgsManager::GetSetting(const std::string& arg) const
855 {
856 LOCK(cs_args);
857 return util::GetSetting(
858 m_settings, m_network, SettingName(arg), !UseDefaultSection(arg), /* get_chain_name= */ false);
859 }
860
GetSettingsList(const std::string & arg) const861 std::vector<util::SettingsValue> ArgsManager::GetSettingsList(const std::string& arg) const
862 {
863 LOCK(cs_args);
864 return util::GetSettingsList(m_settings, m_network, SettingName(arg), !UseDefaultSection(arg));
865 }
866
logArgsPrefix(const std::string & prefix,const std::string & section,const std::map<std::string,std::vector<util::SettingsValue>> & args) const867 void ArgsManager::logArgsPrefix(
868 const std::string& prefix,
869 const std::string& section,
870 const std::map<std::string, std::vector<util::SettingsValue>>& args) const
871 {
872 std::string section_str = section.empty() ? "" : "[" + section + "] ";
873 for (const auto& arg : args) {
874 for (const auto& value : arg.second) {
875 Optional<unsigned int> flags = GetArgFlags('-' + arg.first);
876 if (flags) {
877 std::string value_str = (*flags & SENSITIVE) ? "****" : value.write();
878 LogPrintf("%s %s%s=%s\n", prefix, section_str, arg.first, value_str);
879 }
880 }
881 }
882 }
883
LogArgs() const884 void ArgsManager::LogArgs() const
885 {
886 LOCK(cs_args);
887 for (const auto& section : m_settings.ro_config) {
888 logArgsPrefix("Config file arg:", section.first, section.second);
889 }
890 logArgsPrefix("Command-line arg:", "", m_settings.command_line_options);
891 }
892
getArgsList(const std::vector<std::string> & paramListType) const893 std::map<std::string, std::vector<std::string>> ArgsManager::getArgsList(const std::vector<std::string>& paramListType) const
894 {
895 // Get argument list
896 std::map<std::string, bool> args;
897 for (const auto& arg : m_settings.forced_settings) {
898 args[arg.first] = true;
899 }
900 for (const auto& arg : m_settings.command_line_options) {
901 args[arg.first] = true;
902 }
903 for (const auto& arg : m_settings.ro_config) {
904 for(const auto& confArg : arg.second)
905 args[confArg.first] = true;
906 }
907
908 // Fill argument list with values
909 std::map<std::string, std::vector<std::string>> ret;
910 for (const auto& arg : args) {
911 std::string paramName = '-' + arg.first;
912 std::vector<std::string> paramValue;
913 bool isList = std::find(std::begin(paramListType), std::end(paramListType), paramName) != std::end(paramListType);
914 if(isList) {
915 paramValue = GetArgs(paramName);
916 }
917 else {
918 paramValue.push_back(GetArg(paramName, ""));
919 }
920 ret[arg.first] = paramValue;
921 }
922
923 return ret;
924 }
925
RenameOver(fs::path src,fs::path dest)926 bool RenameOver(fs::path src, fs::path dest)
927 {
928 #ifdef WIN32
929 return MoveFileExW(src.wstring().c_str(), dest.wstring().c_str(),
930 MOVEFILE_REPLACE_EXISTING) != 0;
931 #else
932 int rc = std::rename(src.string().c_str(), dest.string().c_str());
933 return (rc == 0);
934 #endif /* WIN32 */
935 }
936
937 /**
938 * Ignores exceptions thrown by Boost's create_directories if the requested directory exists.
939 * Specifically handles case where path p exists, but it wasn't possible for the user to
940 * write to the parent directory.
941 */
TryCreateDirectories(const fs::path & p)942 bool TryCreateDirectories(const fs::path& p)
943 {
944 try
945 {
946 return fs::create_directories(p);
947 } catch (const fs::filesystem_error&) {
948 if (!fs::exists(p) || !fs::is_directory(p))
949 throw;
950 }
951
952 // create_directories didn't create the directory, it had to have existed already
953 return false;
954 }
955
FileCommit(FILE * file)956 bool FileCommit(FILE *file)
957 {
958 if (fflush(file) != 0) { // harmless if redundantly called
959 LogPrintf("%s: fflush failed: %d\n", __func__, errno);
960 return false;
961 }
962 #ifdef WIN32
963 HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
964 if (FlushFileBuffers(hFile) == 0) {
965 LogPrintf("%s: FlushFileBuffers failed: %d\n", __func__, GetLastError());
966 return false;
967 }
968 #else
969 #if defined(__linux__) || defined(__NetBSD__)
970 if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync
971 LogPrintf("%s: fdatasync failed: %d\n", __func__, errno);
972 return false;
973 }
974 #elif defined(MAC_OSX) && defined(F_FULLFSYNC)
975 if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success
976 LogPrintf("%s: fcntl F_FULLFSYNC failed: %d\n", __func__, errno);
977 return false;
978 }
979 #else
980 if (fsync(fileno(file)) != 0 && errno != EINVAL) {
981 LogPrintf("%s: fsync failed: %d\n", __func__, errno);
982 return false;
983 }
984 #endif
985 #endif
986 return true;
987 }
988
TruncateFile(FILE * file,unsigned int length)989 bool TruncateFile(FILE *file, unsigned int length) {
990 #if defined(WIN32)
991 return _chsize(_fileno(file), length) == 0;
992 #else
993 return ftruncate(fileno(file), length) == 0;
994 #endif
995 }
996
997 /**
998 * this function tries to raise the file descriptor limit to the requested number.
999 * It returns the actual file descriptor limit (which may be more or less than nMinFD)
1000 */
RaiseFileDescriptorLimit(int nMinFD)1001 int RaiseFileDescriptorLimit(int nMinFD) {
1002 #if defined(WIN32)
1003 return 2048;
1004 #else
1005 struct rlimit limitFD;
1006 if (getrlimit(RLIMIT_NOFILE, &limitFD) != -1) {
1007 if (limitFD.rlim_cur < (rlim_t)nMinFD) {
1008 limitFD.rlim_cur = nMinFD;
1009 if (limitFD.rlim_cur > limitFD.rlim_max)
1010 limitFD.rlim_cur = limitFD.rlim_max;
1011 setrlimit(RLIMIT_NOFILE, &limitFD);
1012 getrlimit(RLIMIT_NOFILE, &limitFD);
1013 }
1014 return limitFD.rlim_cur;
1015 }
1016 return nMinFD; // getrlimit failed, assume it's fine
1017 #endif
1018 }
1019
1020 /**
1021 * this function tries to make a particular range of a file allocated (corresponding to disk space)
1022 * it is advisory, and the range specified in the arguments will never contain live data
1023 */
AllocateFileRange(FILE * file,unsigned int offset,unsigned int length)1024 void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) {
1025 #if defined(WIN32)
1026 // Windows-specific version
1027 HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
1028 LARGE_INTEGER nFileSize;
1029 int64_t nEndPos = (int64_t)offset + length;
1030 nFileSize.u.LowPart = nEndPos & 0xFFFFFFFF;
1031 nFileSize.u.HighPart = nEndPos >> 32;
1032 SetFilePointerEx(hFile, nFileSize, 0, FILE_BEGIN);
1033 SetEndOfFile(hFile);
1034 #elif defined(MAC_OSX)
1035 // OSX specific version
1036 // NOTE: Contrary to other OS versions, the OSX version assumes that
1037 // NOTE: offset is the size of the file.
1038 fstore_t fst;
1039 fst.fst_flags = F_ALLOCATECONTIG;
1040 fst.fst_posmode = F_PEOFPOSMODE;
1041 fst.fst_offset = 0;
1042 fst.fst_length = length; // mac os fst_length takes the # of free bytes to allocate, not desired file size
1043 fst.fst_bytesalloc = 0;
1044 if (fcntl(fileno(file), F_PREALLOCATE, &fst) == -1) {
1045 fst.fst_flags = F_ALLOCATEALL;
1046 fcntl(fileno(file), F_PREALLOCATE, &fst);
1047 }
1048 ftruncate(fileno(file), static_cast<off_t>(offset) + length);
1049 #else
1050 #if defined(__linux__)
1051 // Version using posix_fallocate
1052 off_t nEndPos = (off_t)offset + length;
1053 if (0 == posix_fallocate(fileno(file), 0, nEndPos)) return;
1054 #endif
1055 // Fallback version
1056 // TODO: just write one byte per block
1057 static const char buf[65536] = {};
1058 if (fseek(file, offset, SEEK_SET)) {
1059 return;
1060 }
1061 while (length > 0) {
1062 unsigned int now = 65536;
1063 if (length < now)
1064 now = length;
1065 fwrite(buf, 1, now, file); // allowed to fail; this function is advisory anyway
1066 length -= now;
1067 }
1068 #endif
1069 }
1070
1071 #ifdef WIN32
GetSpecialFolderPath(int nFolder,bool fCreate)1072 fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
1073 {
1074 WCHAR pszPath[MAX_PATH] = L"";
1075
1076 if(SHGetSpecialFolderPathW(nullptr, pszPath, nFolder, fCreate))
1077 {
1078 return fs::path(pszPath);
1079 }
1080
1081 LogPrintf("SHGetSpecialFolderPathW() failed, could not obtain requested path.\n");
1082 return fs::path("");
1083 }
1084 #endif
1085
1086 #ifndef WIN32
ShellEscape(const std::string & arg)1087 std::string ShellEscape(const std::string& arg)
1088 {
1089 std::string escaped = arg;
1090 boost::replace_all(escaped, "'", "'\"'\"'");
1091 return "'" + escaped + "'";
1092 }
1093 #endif
1094
1095 #if HAVE_SYSTEM
runCommand(const std::string & strCommand)1096 void runCommand(const std::string& strCommand)
1097 {
1098 if (strCommand.empty()) return;
1099 #ifndef WIN32
1100 int nErr = ::system(strCommand.c_str());
1101 #else
1102 int nErr = ::_wsystem(std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().from_bytes(strCommand).c_str());
1103 #endif
1104 if (nErr)
1105 LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
1106 }
1107 #endif
1108
SetupEnvironment()1109 void SetupEnvironment()
1110 {
1111 #ifdef HAVE_MALLOPT_ARENA_MAX
1112 // glibc-specific: On 32-bit systems set the number of arenas to 1.
1113 // By default, since glibc 2.10, the C library will create up to two heap
1114 // arenas per core. This is known to cause excessive virtual address space
1115 // usage in our usage. Work around it by setting the maximum number of
1116 // arenas to 1.
1117 if (sizeof(void*) == 4) {
1118 mallopt(M_ARENA_MAX, 1);
1119 }
1120 #endif
1121 // On most POSIX systems (e.g. Linux, but not BSD) the environment's locale
1122 // may be invalid, in which case the "C.UTF-8" locale is used as fallback.
1123 #if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
1124 try {
1125 std::locale(""); // Raises a runtime error if current locale is invalid
1126 } catch (const std::runtime_error&) {
1127 setenv("LC_ALL", "C.UTF-8", 1);
1128 }
1129 #elif defined(WIN32)
1130 // Set the default input/output charset is utf-8
1131 SetConsoleCP(CP_UTF8);
1132 SetConsoleOutputCP(CP_UTF8);
1133 #endif
1134 // The path locale is lazy initialized and to avoid deinitialization errors
1135 // in multithreading environments, it is set explicitly by the main thread.
1136 // A dummy locale is used to extract the internal default locale, used by
1137 // fs::path, which is then used to explicitly imbue the path.
1138 std::locale loc = fs::path::imbue(std::locale::classic());
1139 #ifndef WIN32
1140 fs::path::imbue(loc);
1141 #else
1142 fs::path::imbue(std::locale(loc, new std::codecvt_utf8_utf16<wchar_t>()));
1143 #endif
1144 }
1145
SetupNetworking()1146 bool SetupNetworking()
1147 {
1148 #ifdef WIN32
1149 // Initialize Windows Sockets
1150 WSADATA wsadata;
1151 int ret = WSAStartup(MAKEWORD(2,2), &wsadata);
1152 if (ret != NO_ERROR || LOBYTE(wsadata.wVersion ) != 2 || HIBYTE(wsadata.wVersion) != 2)
1153 return false;
1154 #endif
1155 return true;
1156 }
1157
GetNumCores()1158 int GetNumCores()
1159 {
1160 return std::thread::hardware_concurrency();
1161 }
1162
CopyrightHolders(const std::string & strPrefix)1163 std::string CopyrightHolders(const std::string& strPrefix)
1164 {
1165 const auto copyright_devs = strprintf(_(COPYRIGHT_HOLDERS).translated, COPYRIGHT_HOLDERS_SUBSTITUTION);
1166 std::string strCopyrightHolders = strPrefix + copyright_devs;
1167
1168 // Make sure Bitcoin Core copyright is not removed by accident
1169 if (copyright_devs.find("Qtum Core") == std::string::npos) {
1170 strCopyrightHolders += "\n" + strPrefix + "The Qtum Core Developers";
1171 }
1172 return strCopyrightHolders;
1173 }
1174
CheckHex(const std::string & str)1175 bool CheckHex(const std::string& str) {
1176 size_t data=0;
1177 if(str.size() > 2 && (str.compare(0, 2, "0x") == 0 || str.compare(0, 2, "0X") == 0)){
1178 data=2;
1179 }
1180 return str.size() > data && str.find_first_not_of("0123456789abcdefABCDEF", data) == std::string::npos;
1181 }
1182
1183 // Obtain the application startup time (used for uptime calculation)
GetStartupTime()1184 int64_t GetStartupTime()
1185 {
1186 return nStartupTime;
1187 }
1188
AbsPathForConfigVal(const fs::path & path,bool net_specific)1189 fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific)
1190 {
1191 if (path.is_absolute()) {
1192 return path;
1193 }
1194 return fs::absolute(path, GetDataDir(net_specific));
1195 }
1196
ScheduleBatchPriority()1197 void ScheduleBatchPriority()
1198 {
1199 #ifdef SCHED_BATCH
1200 const static sched_param param{};
1201 const int rc = pthread_setschedparam(pthread_self(), SCHED_BATCH, ¶m);
1202 if (rc != 0) {
1203 LogPrintf("Failed to pthread_setschedparam: %s\n", strerror(rc));
1204 }
1205 #endif
1206 }
1207
toHexString(int64_t intValue)1208 std::string toHexString(int64_t intValue) {
1209 //Store big endian representation in a vector
1210 uint64_t num = (uint64_t)intValue;
1211 std::vector<unsigned char> bigEndian;
1212 for(int i=sizeof(num) -1; i>=0; i--){
1213 bigEndian.push_back( (num>>(8*i)) & 0xff );
1214 }
1215
1216 //Convert the vector into hex string
1217 return "0x" + HexStr(bigEndian.begin(), bigEndian.end());
1218 }
1219
ReplaceInt(const int64_t & number,const std::string & key,std::string & str)1220 void ReplaceInt(const int64_t& number, const std::string& key, std::string& str)
1221 {
1222 // Convert the number into hex string
1223 std::string num_hex = toHexString(number);
1224
1225 // Search for key in str and replace it with the hex string
1226 std::string str_replaced = std::regex_replace(str, std::regex(key), num_hex);
1227 str = str_replaced;
1228 }
1229
1230 namespace util {
1231 #ifdef WIN32
WinCmdLineArgs()1232 WinCmdLineArgs::WinCmdLineArgs()
1233 {
1234 wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
1235 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> utf8_cvt;
1236 argv = new char*[argc];
1237 args.resize(argc);
1238 for (int i = 0; i < argc; i++) {
1239 args[i] = utf8_cvt.to_bytes(wargv[i]);
1240 argv[i] = &*args[i].begin();
1241 }
1242 LocalFree(wargv);
1243 }
1244
~WinCmdLineArgs()1245 WinCmdLineArgs::~WinCmdLineArgs()
1246 {
1247 delete[] argv;
1248 }
1249
get()1250 std::pair<int, char**> WinCmdLineArgs::get()
1251 {
1252 return std::make_pair(argc, argv);
1253 }
1254 #endif
1255 } // namespace util
1256