2011-05-14 17:25:05 +02:00
// Copyright (c) 2009-2010 Satoshi Nakamoto
2022-12-24 23:49:50 +00:00
// Copyright (c) 2009-2022 The Bitcoin Core developers
2014-11-17 11:04:01 +08:00
// Distributed under the MIT software license, see the accompanying
2012-05-18 22:02:28 +08:00
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2012-03-31 15:22:45 +02:00
2018-10-22 15:51:11 -07:00
# include <util/system.h>
2013-04-13 00:13:08 -05:00
2017-11-10 13:57:53 +13:00
# include <chainparamsbase.h>
2020-06-11 08:58:46 +02:00
# include <fs.h>
2021-01-13 09:00:43 +01:00
# include <sync.h>
# include <util/check.h>
2021-02-01 13:35:28 +01:00
# include <util/getuniquepath.h>
2018-10-22 15:51:11 -07:00
# include <util/strencodings.h>
2019-12-11 09:55:00 +00:00
# include <util/string.h>
2022-04-20 16:17:19 +02:00
# include <util/syserror.h>
2019-06-17 10:56:52 +03:00
# include <util/translation.h>
2013-04-13 00:13:08 -05:00
2015-03-27 13:19:49 +01:00
# if (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__))
# include <pthread.h>
# include <pthread_np.h>
# endif
2013-01-28 00:07:51 +01:00
# ifndef WIN32
2020-03-26 10:59:46 +01:00
// for posix_fallocate, in configure.ac we check if it is present after this
2014-08-12 12:05:03 +02:00
# ifdef __linux__
2013-04-13 00:13:08 -05:00
# ifdef _POSIX_C_SOURCE
# undef _POSIX_C_SOURCE
# endif
2013-01-28 00:07:51 +01:00
# define _POSIX_C_SOURCE 200112L
2013-04-13 00:13:08 -05:00
2014-08-12 12:05:03 +02:00
# endif // __linux__
2013-04-13 00:13:08 -05:00
2013-07-17 12:20:09 +10:00
# include <algorithm>
2020-10-16 18:06:14 +03:00
# include <cassert>
2013-01-28 00:07:51 +01:00
# include <fcntl.h>
2018-03-06 12:50:20 -05:00
# include <sched.h>
2013-04-26 00:46:47 +02:00
# include <sys/resource.h>
2013-04-13 00:13:08 -05:00
# include <sys/stat.h>
2012-04-15 11:42:40 +02:00
2013-04-13 00:13:08 -05:00
# else
2012-04-15 22:10:54 +02:00
2018-08-06 01:03:33 +00:00
# include <codecvt>
2013-04-13 00:13:08 -05:00
2012-05-17 18:30:09 +02:00
# include <io.h> /* for _commit */
2018-08-05 16:38:25 +00:00
# include <shellapi.h>
2013-04-13 00:13:08 -05:00
# include <shlobj.h>
2012-04-15 22:10:54 +02:00
# endif
2011-05-14 17:25:05 +02:00
2017-03-30 09:37:47 +02:00
# ifdef HAVE_MALLOPT_ARENA_MAX
# include <malloc.h>
# endif
2020-06-11 08:58:46 +02:00
# include <univalue.h>
# include <fstream>
# include <map>
# include <memory>
2021-08-21 14:06:49 -04:00
# include <optional>
2020-06-11 08:58:46 +02:00
# include <string>
2020-03-14 20:58:55 +01:00
# include <system_error>
2017-04-25 09:34:23 +08:00
# include <thread>
2020-01-02 20:54:15 +01:00
# include <typeinfo>
2013-04-13 00:13:08 -05:00
2017-05-14 19:18:26 +01:00
// Application startup time (used for uptime calculation)
const int64_t nStartupTime = GetTime ( ) ;
2011-05-14 17:25:05 +02:00
2015-06-27 19:21:41 +00:00
const char * const BITCOIN_CONF_FILENAME = " bitcoin.conf " ;
2019-04-28 19:08:26 -04:00
const char * const BITCOIN_SETTINGS_FILENAME = " settings.json " ;
2015-06-27 19:21:41 +00:00
2017-05-06 01:36:47 +02:00
ArgsManager gArgs ;
2016-11-29 01:00:11 +00:00
2020-02-11 16:15:37 +10:00
/** Mutex to protect dir_locks. */
2022-04-20 17:10:13 +10:00
static GlobalMutex cs_dir_locks ;
2018-02-13 13:53:17 +01:00
/** A map that contains all the currently held directory locks. After
* successful locking , these will be held here until the global destructor
* cleans them up and thus automatically unlocks them , or ReleaseDirectoryLocks
* is called .
*/
2020-02-11 16:15:37 +10:00
static std : : map < std : : string , std : : unique_ptr < fsbridge : : FileLock > > dir_locks GUARDED_BY ( cs_dir_locks ) ;
2018-02-13 13:53:17 +01:00
2022-03-03 14:40:18 -05:00
bool LockDirectory ( const fs : : path & directory , const fs : : path & lockfile_name , bool probe_only )
2017-12-26 19:41:55 +13:00
{
2020-02-11 16:15:37 +10:00
LOCK ( cs_dir_locks ) ;
2017-12-26 19:41:55 +13:00
fs : : path pathLockFile = directory / lockfile_name ;
2018-02-13 14:12:30 +01:00
// If a lock for this directory already exists in the map, don't try to re-lock it
2021-09-10 00:17:20 -04:00
if ( dir_locks . count ( fs : : PathToString ( pathLockFile ) ) ) {
2018-02-13 14:12:30 +01:00
return true ;
}
// Create empty lock file if it doesn't exist.
FILE * file = fsbridge : : fopen ( pathLockFile , " a " ) ;
2017-12-26 19:41:55 +13:00
if ( file ) fclose ( file ) ;
2021-03-10 17:28:08 +08:00
auto lock = std : : make_unique < fsbridge : : FileLock > ( pathLockFile ) ;
2018-07-25 17:33:22 +08:00
if ( ! lock - > TryLock ( ) ) {
2021-09-10 00:17:20 -04:00
return error ( " Error while attempting to lock directory %s: %s " , fs : : PathToString ( directory ) , lock - > GetReason ( ) ) ;
2018-07-25 17:33:22 +08:00
}
if ( ! probe_only ) {
// Lock successful and we're not just probing, put it into the map
2021-09-10 00:17:20 -04:00
dir_locks . emplace ( fs : : PathToString ( pathLockFile ) , std : : move ( lock ) ) ;
2017-12-26 19:41:55 +13:00
}
return true ;
}
2022-03-03 14:40:18 -05:00
void UnlockDirectory ( const fs : : path & directory , const fs : : path & lockfile_name )
2019-01-31 00:05:18 +00:00
{
2020-02-11 16:15:37 +10:00
LOCK ( cs_dir_locks ) ;
2021-09-10 00:17:20 -04:00
dir_locks . erase ( fs : : PathToString ( directory / lockfile_name ) ) ;
2019-01-31 00:05:18 +00:00
}
2018-02-13 13:53:17 +01:00
void ReleaseDirectoryLocks ( )
{
2020-02-11 16:15:37 +10:00
LOCK ( cs_dir_locks ) ;
2018-02-13 13:53:17 +01:00
dir_locks . clear ( ) ;
}
2018-03-07 03:08:55 -08:00
bool DirIsWritable ( const fs : : path & directory )
{
2021-02-01 13:35:28 +01:00
fs : : path tmpFile = GetUniquePath ( directory ) ;
2018-03-07 03:08:55 -08:00
FILE * file = fsbridge : : fopen ( tmpFile , " a " ) ;
if ( ! file ) return false ;
fclose ( file ) ;
remove ( tmpFile ) ;
return true ;
}
2019-01-24 11:20:57 -08:00
bool CheckDiskSpace ( const fs : : path & dir , uint64_t additional_bytes )
2018-09-01 15:18:02 -07:00
{
2019-01-24 11:20:57 -08:00
constexpr uint64_t min_disk_space = 52428800 ; // 50 MiB
2018-09-01 15:18:02 -07:00
2019-01-24 11:20:57 -08:00
uint64_t free_bytes_available = fs : : space ( dir ) . available ;
return free_bytes_available > = min_disk_space + additional_bytes ;
2018-09-01 15:18:02 -07:00
}
2020-03-20 11:41:11 +01:00
std : : streampos GetFileSize ( const char * path , std : : streamsize max ) {
2020-06-11 08:58:46 +02:00
std : : ifstream file { path , std : : ios : : binary } ;
2020-03-20 11:41:11 +01:00
file . ignore ( max ) ;
return file . gcount ( ) ;
}
2018-03-21 19:24:17 -07:00
/**
* Interpret a string argument as a boolean .
*
2021-09-30 14:18:50 +00:00
* The definition of LocaleIndependentAtoi < int > ( ) requires that non - numeric string values
* like " foo " , return 0. This means that if a user unintentionally supplies a
* non - integer argument here , the return value is always false . This means that
* - foo = false does what the user probably expects , but - foo = true is well defined
* but does not do what they probably expected .
2018-03-21 19:24:17 -07:00
*
2021-09-30 14:18:50 +00:00
* The return value of LocaleIndependentAtoi < int > ( . . . ) is zero when given input not
* representable as an int .
2018-03-21 19:24:17 -07:00
*
* For a more extensive discussion of this topic ( and a wide range of opinions
* on the Right Way to change this code ) , see PR12713 .
*/
2015-06-15 17:17:23 +02:00
static bool InterpretBool ( const std : : string & strValue )
2012-02-16 12:08:32 -08:00
{
2015-06-15 17:17:23 +02:00
if ( strValue . empty ( ) )
return true ;
2021-09-30 14:18:50 +00:00
return ( LocaleIndependentAtoi < int > ( strValue ) ! = 0 ) ;
2015-06-15 17:17:23 +02:00
}
2019-04-22 18:08:51 -04:00
static std : : string SettingName ( const std : : string & arg )
{
return arg . size ( ) > 0 & & arg [ 0 ] = = ' - ' ? arg . substr ( 1 ) : arg ;
}
2021-08-21 14:06:49 -04:00
struct KeyInfo {
std : : string name ;
std : : string section ;
bool negated { false } ;
} ;
2018-03-21 19:24:17 -07:00
/**
2021-08-21 14:06:49 -04:00
* Parse " name " , " section.name " , " noname " , " section.noname " settings keys .
2018-04-04 18:02:00 +10:00
*
2021-08-21 14:06:49 -04:00
* @ note Where an option was negated can be later checked using the
2018-03-21 19:24:17 -07:00
* IsArgNegated ( ) method . One use case for this is to have a way to disable
* options that are not normally boolean ( e . g . using - nodebuglogfile to request
* that debug log output is not sent to any file at all ) .
*/
2021-08-21 14:06:49 -04:00
KeyInfo InterpretKey ( std : : string key )
2015-06-15 17:17:23 +02:00
{
2021-08-21 14:06:49 -04:00
KeyInfo result ;
2019-04-22 18:08:51 -04:00
// Split section name from key name for keys like "testnet.foo" or "regtest.bar"
2018-04-04 18:03:00 +10:00
size_t option_index = key . find ( ' . ' ) ;
2019-04-22 18:08:51 -04:00
if ( option_index ! = std : : string : : npos ) {
2021-08-21 14:06:49 -04:00
result . section = key . substr ( 0 , option_index ) ;
2019-04-22 18:08:51 -04:00
key . erase ( 0 , option_index + 1 ) ;
}
if ( key . substr ( 0 , 2 ) = = " no " ) {
key . erase ( 0 , 2 ) ;
2021-08-21 14:06:49 -04:00
result . negated = true ;
2019-04-22 18:08:51 -04:00
}
2021-08-21 14:06:49 -04:00
result . name = key ;
return result ;
2019-04-22 18:08:51 -04:00
}
/**
2021-08-21 14:06:49 -04:00
* Interpret settings value based on registered flags .
*
* @ param [ in ] key key information to know if key was negated
* @ param [ in ] value string value of setting to be parsed
* @ param [ in ] flags ArgsManager registered argument flags
* @ param [ out ] error Error description if settings value is not valid
2019-04-22 18:08:51 -04:00
*
2021-08-21 14:06:49 -04:00
* @ return parsed settings value if it is valid , otherwise nullopt accompanied
* by a descriptive error string
2019-04-22 18:08:51 -04:00
*/
2022-04-12 03:00:28 -04:00
static std : : optional < util : : SettingsValue > InterpretValue ( const KeyInfo & key , const std : : string * value ,
2021-08-21 14:06:49 -04:00
unsigned int flags , std : : string & error )
{
// Return negated settings as false values.
if ( key . negated ) {
2021-08-21 14:06:49 -04:00
if ( flags & ArgsManager : : DISALLOW_NEGATION ) {
2021-08-21 14:06:49 -04:00
error = strprintf ( " Negating of -%s is meaningless and therefore forbidden " , key . name ) ;
return std : : nullopt ;
}
// Double negatives like -nofoo=0 are supported (but discouraged)
2022-04-12 03:00:28 -04:00
if ( value & & ! InterpretBool ( * value ) ) {
LogPrintf ( " Warning: parsed potentially confusing double-negative -%s=%s \n " , key . name , * value ) ;
2021-08-21 14:06:49 -04:00
return true ;
}
2019-04-22 18:08:51 -04:00
return false ;
2012-02-16 12:08:32 -08:00
}
2022-04-12 03:00:28 -04:00
if ( ! value & & ( flags & ArgsManager : : DISALLOW_ELISION ) ) {
error = strprintf ( " Can not set -%s with no value. Please specify value with -%s=value. " , key . name , key . name ) ;
return std : : nullopt ;
}
return value ? * value : " " ;
2012-02-16 12:08:32 -08:00
}
2020-04-16 12:26:01 -04:00
// Define default constructor and destructor that are not inline, so code instantiating this class doesn't need to
// #include class definitions for all members.
// For example, m_settings has an internal dependency on univalue.
2022-05-11 16:02:15 +01:00
ArgsManager : : ArgsManager ( ) = default ;
ArgsManager : : ~ ArgsManager ( ) = default ;
2018-04-04 18:06:00 +10:00
2023-01-20 16:25:14 +01:00
std : : set < std : : string > ArgsManager : : GetUnsuitableSectionOnlyArgs ( ) const
2018-04-04 18:07:00 +10:00
{
2018-11-12 11:06:36 +09:00
std : : set < std : : string > unsuitables ;
2018-08-27 23:19:18 +02:00
LOCK ( cs_args ) ;
2018-04-04 18:07:00 +10:00
// if there's no section selected, don't worry
2018-11-12 11:06:36 +09:00
if ( m_network . empty ( ) ) return std : : set < std : : string > { } ;
2018-04-04 18:07:00 +10:00
// if it's okay to use the default section for this network, don't worry
2018-11-12 11:06:36 +09:00
if ( m_network = = CBaseChainParams : : MAIN ) return std : : set < std : : string > { } ;
2018-04-04 18:07:00 +10:00
for ( const auto & arg : m_network_only_args ) {
2019-04-22 18:08:51 -04:00
if ( OnlyHasDefaultSectionSetting ( m_settings , m_network , SettingName ( arg ) ) ) {
unsuitables . insert ( arg ) ;
}
2018-04-04 18:07:00 +10:00
}
2018-11-12 11:06:36 +09:00
return unsuitables ;
}
2023-01-20 16:25:14 +01:00
std : : list < SectionInfo > ArgsManager : : GetUnrecognizedSections ( ) const
2018-11-12 11:06:36 +09:00
{
// Section names to be recognized in the config file.
static const std : : set < std : : string > available_sections {
CBaseChainParams : : REGTEST ,
2020-03-05 15:58:30 +09:00
CBaseChainParams : : SIGNET ,
2018-11-12 11:06:36 +09:00
CBaseChainParams : : TESTNET ,
CBaseChainParams : : MAIN
} ;
LOCK ( cs_args ) ;
2019-02-04 12:53:19 +09:00
std : : list < SectionInfo > unrecognized = m_config_sections ;
unrecognized . remove_if ( [ ] ( const SectionInfo & appeared ) { return available_sections . find ( appeared . m_name ) ! = available_sections . end ( ) ; } ) ;
return unrecognized ;
2018-04-04 18:07:00 +10:00
}
2018-04-04 18:03:00 +10:00
void ArgsManager : : SelectConfigNetwork ( const std : : string & network )
{
2018-08-27 23:19:18 +02:00
LOCK ( cs_args ) ;
2018-04-04 18:03:00 +10:00
m_network = network ;
}
2018-04-28 19:40:51 -04:00
bool ArgsManager : : ParseParameters ( int argc , const char * const argv [ ] , std : : string & error )
2011-05-14 17:25:05 +02:00
{
2016-11-29 18:52:44 -08:00
LOCK ( cs_args ) ;
2019-04-22 18:08:51 -04:00
m_settings . command_line_options . clear ( ) ;
2018-03-21 19:24:17 -07:00
for ( int i = 1 ; i < argc ; i + + ) {
std : : string key ( argv [ i ] ) ;
2019-10-18 09:12:15 +03:00
# ifdef MAC_OSX
// At the first time when a user gets the "App downloaded from the
// internet" warning, and clicks the Open button, macOS passes
// a unique process serial number (PSN) as -psn_... command-line
// argument, which we filter out.
if ( key . substr ( 0 , 5 ) = = " -psn_ " ) continue ;
# endif
2019-07-27 14:41:27 +03:00
if ( key = = " - " ) break ; //bitcoin-tx using stdin
2022-04-12 03:00:28 -04:00
std : : optional < std : : string > val ;
2018-03-21 19:24:17 -07:00
size_t is_index = key . find ( ' = ' ) ;
if ( is_index ! = std : : string : : npos ) {
val = key . substr ( is_index + 1 ) ;
key . erase ( is_index ) ;
2011-05-14 17:25:05 +02:00
}
2012-10-02 21:36:39 +02:00
# ifdef WIN32
2019-08-07 13:42:54 +09:00
key = ToLower ( key ) ;
2018-03-21 19:24:17 -07:00
if ( key [ 0 ] = = ' / ' )
key [ 0 ] = ' - ' ;
2012-10-02 21:36:39 +02:00
# endif
2014-06-04 07:36:45 +03:00
2021-01-13 09:00:43 +01:00
if ( key [ 0 ] ! = ' - ' ) {
if ( ! m_accept_any_command & & m_command . empty ( ) ) {
// The first non-dash arg is a registered command
2021-03-15 10:41:30 +08:00
std : : optional < unsigned int > flags = GetArgFlags ( key ) ;
2021-01-13 09:00:43 +01:00
if ( ! flags | | ! ( * flags & ArgsManager : : COMMAND ) ) {
error = strprintf ( " Invalid command '%s' " , argv [ i ] ) ;
return false ;
}
}
m_command . push_back ( key ) ;
while ( + + i < argc ) {
// The remaining args are command args
m_command . push_back ( argv [ i ] ) ;
}
2011-05-14 17:25:05 +02:00
break ;
2021-01-13 09:00:43 +01:00
}
2012-02-06 13:55:11 -05:00
2018-03-21 19:24:17 -07:00
// Transform --foo to -foo
if ( key . length ( ) > 1 & & key [ 1 ] = = ' - ' )
key . erase ( 0 , 1 ) ;
2019-04-22 18:08:51 -04:00
// Transform -foo to foo
key . erase ( 0 , 1 ) ;
2021-08-21 14:06:49 -04:00
KeyInfo keyinfo = InterpretKey ( key ) ;
std : : optional < unsigned int > flags = GetArgFlags ( ' - ' + keyinfo . name ) ;
2019-12-19 16:27:15 -05:00
// Unknown command line options and command line options with dot
2021-08-21 14:06:49 -04:00
// characters (which are returned from InterpretKey with nonempty
2019-12-19 16:27:15 -05:00
// section strings) are not valid.
2021-08-21 14:06:49 -04:00
if ( ! flags | | ! keyinfo . section . empty ( ) ) {
2019-12-19 16:27:15 -05:00
error = strprintf ( " Invalid parameter %s " , argv [ i ] ) ;
2019-07-27 14:41:27 +03:00
return false ;
2018-04-28 19:40:51 -04:00
}
2019-12-19 16:27:15 -05:00
2022-04-12 03:00:28 -04:00
std : : optional < util : : SettingsValue > value = InterpretValue ( keyinfo , val ? & * val : nullptr , * flags , error ) ;
2021-08-21 14:06:49 -04:00
if ( ! value ) return false ;
2019-12-19 16:27:15 -05:00
2021-08-21 14:06:49 -04:00
m_settings . command_line_options [ keyinfo . name ] . push_back ( * value ) ;
2011-05-14 17:25:05 +02:00
}
2018-04-18 14:35:44 +09:00
2021-06-03 12:14:33 +02:00
// we do not allow -includeconf from command line, only -noincludeconf
2019-04-22 18:08:51 -04:00
if ( auto * includes = util : : FindKey ( m_settings . command_line_options , " includeconf " ) ) {
2021-06-03 12:14:33 +02:00
const util : : SettingsSpan values { * includes } ;
// Range may be empty if -noincludeconf was passed
if ( ! values . empty ( ) ) {
error = " -includeconf cannot be used from commandline; -includeconf= " + values . begin ( ) - > write ( ) ;
return false ; // pick first value as example
}
2018-04-18 14:35:44 +09:00
}
2021-05-20 13:46:15 +02:00
return true ;
2018-04-28 19:40:51 -04:00
}
2021-03-15 10:41:30 +08:00
std : : optional < unsigned int > ArgsManager : : GetArgFlags ( const std : : string & name ) const
2018-04-28 19:40:51 -04:00
{
2018-08-27 23:19:18 +02:00
LOCK ( cs_args ) ;
2018-04-28 19:40:51 -04:00
for ( const auto & arg_map : m_available_args ) {
2019-11-11 19:05:12 -05:00
const auto search = arg_map . second . find ( name ) ;
2019-07-27 14:41:27 +03:00
if ( search ! = arg_map . second . end ( ) ) {
return search - > second . m_flags ;
}
2018-04-28 19:40:51 -04:00
}
2021-03-15 10:41:30 +08:00
return std : : nullopt ;
2011-05-14 17:25:05 +02:00
}
2022-02-09 17:49:42 -05:00
fs : : path ArgsManager : : GetPathArg ( std : : string arg , const fs : : path & default_value ) const
2022-02-04 17:57:51 +02:00
{
2022-02-09 17:49:42 -05:00
if ( IsArgNegated ( arg ) ) return fs : : path { } ;
std : : string path_str = GetArg ( arg , " " ) ;
if ( path_str . empty ( ) ) return default_value ;
fs : : path result = fs : : PathFromString ( path_str ) . lexically_normal ( ) ;
2022-02-04 17:57:51 +02:00
// Remove trailing slash, if present.
return result . has_filename ( ) ? result : result . parent_path ( ) ;
}
2021-05-20 22:28:06 +02:00
const fs : : path & ArgsManager : : GetBlocksDirPath ( ) const
2021-04-09 08:16:36 +02:00
{
LOCK ( cs_args ) ;
fs : : path & path = m_cached_blocks_path ;
// Cache the path to avoid calling fs::create_directories on every call of
// this function
if ( ! path . empty ( ) ) return path ;
if ( IsArgSet ( " -blocksdir " ) ) {
2022-02-04 18:52:10 +02:00
path = fs : : absolute ( GetPathArg ( " -blocksdir " ) ) ;
2021-04-09 08:16:36 +02:00
if ( ! fs : : is_directory ( path ) ) {
path = " " ;
return path ;
}
} else {
2021-05-22 14:55:38 +02:00
path = GetDataDirBase ( ) ;
2021-04-09 08:16:36 +02:00
}
2021-09-10 00:17:20 -04:00
path / = fs : : PathFromString ( BaseParams ( ) . DataDir ( ) ) ;
2021-04-09 08:16:36 +02:00
path / = " blocks " ;
fs : : create_directories ( path ) ;
return path ;
}
2021-05-22 15:01:04 +02:00
const fs : : path & ArgsManager : : GetDataDir ( bool net_specific ) const
2021-04-08 23:15:01 +02:00
{
LOCK ( cs_args ) ;
fs : : path & path = net_specific ? m_cached_network_datadir_path : m_cached_datadir_path ;
// Cache the path to avoid calling fs::create_directories on every call of
// this function
if ( ! path . empty ( ) ) return path ;
2022-02-04 18:23:50 +02:00
const fs : : path datadir { GetPathArg ( " -datadir " ) } ;
2021-04-08 23:15:01 +02:00
if ( ! datadir . empty ( ) ) {
2022-02-04 18:23:50 +02:00
path = fs : : absolute ( datadir ) ;
2021-04-08 23:15:01 +02:00
if ( ! fs : : is_directory ( path ) ) {
path = " " ;
return path ;
}
} else {
path = GetDefaultDataDir ( ) ;
}
2022-02-04 22:53:04 +02:00
if ( ! fs : : exists ( path ) ) {
2021-04-08 23:15:01 +02:00
fs : : create_directories ( path / " wallets " ) ;
}
2022-02-04 22:53:04 +02:00
if ( net_specific & & ! BaseParams ( ) . DataDir ( ) . empty ( ) ) {
path / = fs : : PathFromString ( BaseParams ( ) . DataDir ( ) ) ;
if ( ! fs : : exists ( path ) ) {
fs : : create_directories ( path / " wallets " ) ;
}
}
2021-04-08 23:15:01 +02:00
return path ;
}
2021-02-26 09:52:50 +01:00
void ArgsManager : : ClearPathCache ( )
2021-04-08 23:15:01 +02:00
{
LOCK ( cs_args ) ;
m_cached_datadir_path = fs : : path ( ) ;
m_cached_network_datadir_path = fs : : path ( ) ;
2021-04-09 08:16:36 +02:00
m_cached_blocks_path = fs : : path ( ) ;
2021-04-08 23:15:01 +02:00
}
2021-01-13 09:00:43 +01:00
std : : optional < const ArgsManager : : Command > ArgsManager : : GetCommand ( ) const
{
Command ret ;
LOCK ( cs_args ) ;
auto it = m_command . begin ( ) ;
if ( it = = m_command . end ( ) ) {
// No command was passed
return std : : nullopt ;
}
if ( ! m_accept_any_command ) {
// The registered command
ret . command = * ( it + + ) ;
}
while ( it ! = m_command . end ( ) ) {
// The unregistered command and args (if any)
ret . args . push_back ( * ( it + + ) ) ;
}
return ret ;
}
2017-07-22 00:00:52 +01:00
std : : vector < std : : string > ArgsManager : : GetArgs ( const std : : string & strArg ) const
2017-05-06 01:36:47 +02:00
{
2019-04-22 18:08:51 -04:00
std : : vector < std : : string > result ;
2019-11-12 13:56:18 -05:00
for ( const util : : SettingsValue & value : GetSettingsList ( strArg ) ) {
2019-04-22 18:08:51 -04:00
result . push_back ( value . isFalse ( ) ? " 0 " : value . isTrue ( ) ? " 1 " : value . get_str ( ) ) ;
2018-04-04 18:06:00 +10:00
}
2018-04-04 18:01:00 +10:00
return result ;
2017-05-06 01:36:47 +02:00
}
2017-07-22 00:00:52 +01:00
bool ArgsManager : : IsArgSet ( const std : : string & strArg ) const
2016-11-29 17:51:30 -08:00
{
2019-11-12 13:47:19 -05:00
return ! GetSetting ( strArg ) . isNull ( ) ;
2016-11-29 17:51:30 -08:00
}
2019-04-28 19:08:26 -04:00
bool ArgsManager : : InitSettings ( std : : string & error )
{
if ( ! GetSettingsPath ( ) ) {
return true ; // Do nothing if settings file disabled.
}
std : : vector < std : : string > errors ;
if ( ! ReadSettingsFile ( & errors ) ) {
2021-08-06 21:59:14 +03:00
error = strprintf ( " Failed loading settings file: \n %s \n " , MakeUnorderedList ( errors ) ) ;
2019-04-28 19:08:26 -04:00
return false ;
}
if ( ! WriteSettingsFile ( & errors ) ) {
2021-08-06 21:59:14 +03:00
error = strprintf ( " Failed saving settings file: \n %s \n " , MakeUnorderedList ( errors ) ) ;
2019-04-28 19:08:26 -04:00
return false ;
}
return true ;
}
2022-05-16 14:37:00 -04:00
bool ArgsManager : : GetSettingsPath ( fs : : path * filepath , bool temp , bool backup ) const
2019-04-28 19:08:26 -04:00
{
2022-03-25 21:42:58 +01:00
fs : : path settings = GetPathArg ( " -settings " , BITCOIN_SETTINGS_FILENAME ) ;
2022-02-09 17:54:49 -05:00
if ( settings . empty ( ) ) {
2019-04-28 19:08:26 -04:00
return false ;
}
2022-05-16 14:37:00 -04:00
if ( backup ) {
settings + = " .bak " ;
}
2019-04-28 19:08:26 -04:00
if ( filepath ) {
2022-02-09 17:54:49 -05:00
* filepath = fsbridge : : AbsPathJoin ( GetDataDirNet ( ) , temp ? settings + " .tmp " : settings ) ;
2019-04-28 19:08:26 -04:00
}
return true ;
}
static void SaveErrors ( const std : : vector < std : : string > errors , std : : vector < std : : string > * error_out )
{
for ( const auto & error : errors ) {
if ( error_out ) {
error_out - > emplace_back ( error ) ;
} else {
LogPrintf ( " %s \n " , error ) ;
}
}
}
bool ArgsManager : : ReadSettingsFile ( std : : vector < std : : string > * errors )
{
fs : : path path ;
if ( ! GetSettingsPath ( & path , /* temp= */ false ) ) {
return true ; // Do nothing if settings file disabled.
}
LOCK ( cs_args ) ;
m_settings . rw_settings . clear ( ) ;
std : : vector < std : : string > read_errors ;
if ( ! util : : ReadSettings ( path , m_settings . rw_settings , read_errors ) ) {
SaveErrors ( read_errors , errors ) ;
return false ;
}
2020-07-30 10:50:54 +02:00
for ( const auto & setting : m_settings . rw_settings ) {
2021-08-21 14:06:49 -04:00
KeyInfo key = InterpretKey ( setting . first ) ; // Split setting key into section and argname
if ( ! GetArgFlags ( ' - ' + key . name ) ) {
2020-07-30 10:50:54 +02:00
LogPrintf ( " Ignoring unknown rw_settings value %s \n " , setting . first ) ;
}
}
2019-04-28 19:08:26 -04:00
return true ;
}
2022-05-16 14:37:00 -04:00
bool ArgsManager : : WriteSettingsFile ( std : : vector < std : : string > * errors , bool backup ) const
2019-04-28 19:08:26 -04:00
{
fs : : path path , path_tmp ;
2022-05-16 14:37:00 -04:00
if ( ! GetSettingsPath ( & path , /*temp=*/ false , backup ) | | ! GetSettingsPath ( & path_tmp , /*temp=*/ true , backup ) ) {
2019-04-28 19:08:26 -04:00
throw std : : logic_error ( " Attempt to write settings file when dynamic settings are disabled. " ) ;
}
LOCK ( cs_args ) ;
std : : vector < std : : string > write_errors ;
if ( ! util : : WriteSettings ( path_tmp , m_settings . rw_settings , write_errors ) ) {
SaveErrors ( write_errors , errors ) ;
return false ;
}
if ( ! RenameOver ( path_tmp , path ) ) {
2021-09-10 00:17:20 -04:00
SaveErrors ( { strprintf ( " Failed renaming settings file %s to %s \n " , fs : : PathToString ( path_tmp ) , fs : : PathToString ( path ) ) } , errors ) ;
2019-04-28 19:08:26 -04:00
return false ;
}
return true ;
}
2019-04-29 15:29:00 -04:00
util : : SettingsValue ArgsManager : : GetPersistentSetting ( const std : : string & name ) const
{
LOCK ( cs_args ) ;
return util : : GetSetting ( m_settings , m_network , name , ! UseDefaultSection ( " - " + name ) ,
/*ignore_nonpersistent=*/ true , /*get_chain_name=*/ false ) ;
}
2018-03-21 19:24:17 -07:00
bool ArgsManager : : IsArgNegated ( const std : : string & strArg ) const
{
2019-11-12 13:47:19 -05:00
return GetSetting ( strArg ) . isFalse ( ) ;
2018-03-21 19:24:17 -07:00
}
2017-07-22 00:00:52 +01:00
std : : string ArgsManager : : GetArg ( const std : : string & strArg , const std : : string & strDefault ) const
2021-10-24 02:26:19 -04:00
{
return GetArg ( strArg ) . value_or ( strDefault ) ;
}
std : : optional < std : : string > ArgsManager : : GetArg ( const std : : string & strArg ) const
2012-02-06 12:37:49 -05:00
{
2019-11-12 13:47:19 -05:00
const util : : SettingsValue value = GetSetting ( strArg ) ;
2021-10-24 02:26:19 -04:00
return SettingToString ( value ) ;
}
std : : optional < std : : string > SettingToString ( const util : : SettingsValue & value )
{
if ( value . isNull ( ) ) return std : : nullopt ;
if ( value . isFalse ( ) ) return " 0 " ;
if ( value . isTrue ( ) ) return " 1 " ;
if ( value . isNum ( ) ) return value . getValStr ( ) ;
return value . get_str ( ) ;
2019-04-29 15:29:00 -04:00
}
std : : string SettingToString ( const util : : SettingsValue & value , const std : : string & strDefault )
{
2021-10-24 02:26:19 -04:00
return SettingToString ( value ) . value_or ( strDefault ) ;
2012-02-06 12:37:49 -05:00
}
2019-08-22 21:40:41 -04:00
int64_t ArgsManager : : GetIntArg ( const std : : string & strArg , int64_t nDefault ) const
2021-10-24 02:26:19 -04:00
{
return GetIntArg ( strArg ) . value_or ( nDefault ) ;
}
std : : optional < int64_t > ArgsManager : : GetIntArg ( const std : : string & strArg ) const
2012-02-06 12:37:49 -05:00
{
2019-11-12 13:47:19 -05:00
const util : : SettingsValue value = GetSetting ( strArg ) ;
2021-10-24 02:26:19 -04:00
return SettingToInt ( value ) ;
}
std : : optional < int64_t > SettingToInt ( const util : : SettingsValue & value )
{
if ( value . isNull ( ) ) return std : : nullopt ;
if ( value . isFalse ( ) ) return 0 ;
if ( value . isTrue ( ) ) return 1 ;
if ( value . isNum ( ) ) return value . getInt < int64_t > ( ) ;
return LocaleIndependentAtoi < int64_t > ( value . get_str ( ) ) ;
2019-04-29 15:29:00 -04:00
}
int64_t SettingToInt ( const util : : SettingsValue & value , int64_t nDefault )
{
2021-10-24 02:26:19 -04:00
return SettingToInt ( value ) . value_or ( nDefault ) ;
2012-02-06 12:37:49 -05:00
}
2017-07-22 00:00:52 +01:00
bool ArgsManager : : GetBoolArg ( const std : : string & strArg , bool fDefault ) const
2021-10-24 02:26:19 -04:00
{
return GetBoolArg ( strArg ) . value_or ( fDefault ) ;
}
std : : optional < bool > ArgsManager : : GetBoolArg ( const std : : string & strArg ) const
2012-02-06 12:37:49 -05:00
{
2019-11-12 13:47:19 -05:00
const util : : SettingsValue value = GetSetting ( strArg ) ;
2021-10-24 02:26:19 -04:00
return SettingToBool ( value ) ;
}
std : : optional < bool > SettingToBool ( const util : : SettingsValue & value )
{
if ( value . isNull ( ) ) return std : : nullopt ;
if ( value . isBool ( ) ) return value . get_bool ( ) ;
return InterpretBool ( value . get_str ( ) ) ;
2019-04-29 15:29:00 -04:00
}
bool SettingToBool ( const util : : SettingsValue & value , bool fDefault )
{
2021-10-24 02:26:19 -04:00
return SettingToBool ( value ) . value_or ( fDefault ) ;
2012-02-06 12:37:49 -05:00
}
2017-05-06 01:36:47 +02:00
bool ArgsManager : : SoftSetArg ( const std : : string & strArg , const std : : string & strValue )
2012-01-03 10:14:22 -05:00
{
2016-11-29 18:52:44 -08:00
LOCK ( cs_args ) ;
2017-07-22 00:00:52 +01:00
if ( IsArgSet ( strArg ) ) return false ;
2017-05-06 21:23:21 +02:00
ForceSetArg ( strArg , strValue ) ;
2012-01-03 10:14:22 -05:00
return true ;
}
2017-05-06 01:36:47 +02:00
bool ArgsManager : : SoftSetBoolArg ( const std : : string & strArg , bool fValue )
2012-01-03 10:14:22 -05:00
{
if ( fValue )
return SoftSetArg ( strArg , std : : string ( " 1 " ) ) ;
else
return SoftSetArg ( strArg , std : : string ( " 0 " ) ) ;
}
2017-05-06 01:36:47 +02:00
void ArgsManager : : ForceSetArg ( const std : : string & strArg , const std : : string & strValue )
2016-12-24 11:28:44 -05:00
{
LOCK ( cs_args ) ;
2019-04-22 18:08:51 -04:00
m_settings . forced_settings [ SettingName ( strArg ) ] = strValue ;
2016-12-24 11:28:44 -05:00
}
2021-06-17 14:01:57 +02:00
void ArgsManager : : AddCommand ( const std : : string & cmd , const std : : string & help )
2021-01-13 09:00:43 +01:00
{
Assert ( cmd . find ( ' = ' ) = = std : : string : : npos ) ;
Assert ( cmd . at ( 0 ) ! = ' - ' ) ;
LOCK ( cs_args ) ;
m_accept_any_command = false ; // latch to false
2021-06-17 14:01:57 +02:00
std : : map < std : : string , Arg > & arg_map = m_available_args [ OptionsCategory : : COMMANDS ] ;
2021-01-13 09:00:43 +01:00
auto ret = arg_map . emplace ( cmd , Arg { " " , help , ArgsManager : : COMMAND } ) ;
Assert ( ret . second ) ; // Fail on duplicate commands
}
scripted-diff: Use ArgsManager::DEBUG_ONLY flag
-BEGIN VERIFY SCRIPT-
sed -i 's/unsigned int flags, const bool debug_only,/unsigned int flags,/' src/util/system.h src/util/system.cpp
sed -i 's/ArgsManager::NONE, debug_only/flags, false/' src/util/system.cpp
sed -i 's/arg.second.m_debug_only/(arg.second.m_flags \& ArgsManager::DEBUG_ONLY)/' src/util/system.cpp
sed -i 's/ArgsManager::ALLOW_ANY, true, OptionsCategory::/ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::/' $(git grep --files-with-matches 'AddArg(' src)
sed -i 's/ArgsManager::ALLOW_ANY, false, OptionsCategory::/ArgsManager::ALLOW_ANY, OptionsCategory::/' $(git grep --files-with-matches 'AddArg(' src)
-END VERIFY SCRIPT-
2019-07-27 12:06:32 +03:00
void ArgsManager : : AddArg ( const std : : string & name , const std : : string & help , unsigned int flags , const OptionsCategory & cat )
2018-04-28 16:54:58 -04:00
{
2021-01-13 09:00:43 +01:00
Assert ( ( flags & ArgsManager : : COMMAND ) = = 0 ) ; // use AddCommand
2018-05-16 15:15:18 -04:00
// Split arg name from its help param
size_t eq_index = name . find ( ' = ' ) ;
if ( eq_index = = std : : string : : npos ) {
eq_index = name . size ( ) ;
}
2019-07-27 12:37:09 +03:00
std : : string arg_name = name . substr ( 0 , eq_index ) ;
2018-05-16 15:15:18 -04:00
2018-08-27 23:19:18 +02:00
LOCK ( cs_args ) ;
2018-05-16 15:15:18 -04:00
std : : map < std : : string , Arg > & arg_map = m_available_args [ cat ] ;
2019-07-27 12:37:09 +03:00
auto ret = arg_map . emplace ( arg_name , Arg { name . substr ( eq_index , name . size ( ) - eq_index ) , help , flags } ) ;
2018-05-16 15:15:18 -04:00
assert ( ret . second ) ; // Make sure an insertion actually happened
2019-07-27 12:37:09 +03:00
if ( flags & ArgsManager : : NETWORK_ONLY ) {
m_network_only_args . emplace ( arg_name ) ;
}
2018-04-28 16:54:58 -04:00
}
2018-06-11 14:23:13 -07:00
void ArgsManager : : AddHiddenArgs ( const std : : vector < std : : string > & names )
{
for ( const std : : string & name : names ) {
scripted-diff: Use ArgsManager::DEBUG_ONLY flag
-BEGIN VERIFY SCRIPT-
sed -i 's/unsigned int flags, const bool debug_only,/unsigned int flags,/' src/util/system.h src/util/system.cpp
sed -i 's/ArgsManager::NONE, debug_only/flags, false/' src/util/system.cpp
sed -i 's/arg.second.m_debug_only/(arg.second.m_flags \& ArgsManager::DEBUG_ONLY)/' src/util/system.cpp
sed -i 's/ArgsManager::ALLOW_ANY, true, OptionsCategory::/ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::/' $(git grep --files-with-matches 'AddArg(' src)
sed -i 's/ArgsManager::ALLOW_ANY, false, OptionsCategory::/ArgsManager::ALLOW_ANY, OptionsCategory::/' $(git grep --files-with-matches 'AddArg(' src)
-END VERIFY SCRIPT-
2019-07-27 12:06:32 +03:00
AddArg ( name , " " , ArgsManager : : ALLOW_ANY , OptionsCategory : : HIDDEN ) ;
2018-06-11 14:23:13 -07:00
}
}
2018-07-21 18:53:54 +09:00
std : : string ArgsManager : : GetHelpMessage ( ) const
2018-04-28 16:54:58 -04:00
{
2020-10-06 14:36:50 +03:00
const bool show_debug = GetBoolArg ( " -help-debug " , false ) ;
2018-04-28 16:54:58 -04:00
2022-07-25 11:46:57 +01:00
std : : string usage ;
2018-08-27 23:19:18 +02:00
LOCK ( cs_args ) ;
2018-05-16 15:15:18 -04:00
for ( const auto & arg_map : m_available_args ) {
switch ( arg_map . first ) {
case OptionsCategory : : OPTIONS :
usage + = HelpMessageGroup ( " Options: " ) ;
break ;
case OptionsCategory : : CONNECTION :
2018-05-29 18:49:44 +02:00
usage + = HelpMessageGroup ( " Connection options: " ) ;
2018-05-16 15:15:18 -04:00
break ;
case OptionsCategory : : ZMQ :
2018-05-29 18:49:44 +02:00
usage + = HelpMessageGroup ( " ZeroMQ notification options: " ) ;
2018-05-16 15:15:18 -04:00
break ;
case OptionsCategory : : DEBUG_TEST :
2018-05-29 18:49:44 +02:00
usage + = HelpMessageGroup ( " Debugging/Testing options: " ) ;
2018-05-16 15:15:18 -04:00
break ;
case OptionsCategory : : NODE_RELAY :
2018-05-29 18:49:44 +02:00
usage + = HelpMessageGroup ( " Node relay options: " ) ;
2018-05-16 15:15:18 -04:00
break ;
case OptionsCategory : : BLOCK_CREATION :
2018-05-29 18:49:44 +02:00
usage + = HelpMessageGroup ( " Block creation options: " ) ;
2018-05-16 15:15:18 -04:00
break ;
case OptionsCategory : : RPC :
2018-05-29 18:49:44 +02:00
usage + = HelpMessageGroup ( " RPC server options: " ) ;
2018-05-16 15:15:18 -04:00
break ;
case OptionsCategory : : WALLET :
2018-05-29 18:49:44 +02:00
usage + = HelpMessageGroup ( " Wallet options: " ) ;
2018-05-16 15:15:18 -04:00
break ;
case OptionsCategory : : WALLET_DEBUG_TEST :
if ( show_debug ) usage + = HelpMessageGroup ( " Wallet debugging/testing options: " ) ;
break ;
case OptionsCategory : : CHAINPARAMS :
2018-05-29 18:49:44 +02:00
usage + = HelpMessageGroup ( " Chain selection options: " ) ;
2018-05-16 15:15:18 -04:00
break ;
case OptionsCategory : : GUI :
2018-05-29 18:49:44 +02:00
usage + = HelpMessageGroup ( " UI Options: " ) ;
2018-05-16 15:15:18 -04:00
break ;
case OptionsCategory : : COMMANDS :
2018-05-29 18:49:44 +02:00
usage + = HelpMessageGroup ( " Commands: " ) ;
2018-05-16 15:15:18 -04:00
break ;
case OptionsCategory : : REGISTER_COMMANDS :
2018-05-29 18:49:44 +02:00
usage + = HelpMessageGroup ( " Register Commands: " ) ;
2018-05-16 15:15:18 -04:00
break ;
default :
break ;
2018-04-28 16:54:58 -04:00
}
2018-05-16 15:15:18 -04:00
// When we get to the hidden options, stop
if ( arg_map . first = = OptionsCategory : : HIDDEN ) break ;
for ( const auto & arg : arg_map . second ) {
scripted-diff: Use ArgsManager::DEBUG_ONLY flag
-BEGIN VERIFY SCRIPT-
sed -i 's/unsigned int flags, const bool debug_only,/unsigned int flags,/' src/util/system.h src/util/system.cpp
sed -i 's/ArgsManager::NONE, debug_only/flags, false/' src/util/system.cpp
sed -i 's/arg.second.m_debug_only/(arg.second.m_flags \& ArgsManager::DEBUG_ONLY)/' src/util/system.cpp
sed -i 's/ArgsManager::ALLOW_ANY, true, OptionsCategory::/ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::/' $(git grep --files-with-matches 'AddArg(' src)
sed -i 's/ArgsManager::ALLOW_ANY, false, OptionsCategory::/ArgsManager::ALLOW_ANY, OptionsCategory::/' $(git grep --files-with-matches 'AddArg(' src)
-END VERIFY SCRIPT-
2019-07-27 12:06:32 +03:00
if ( show_debug | | ! ( arg . second . m_flags & ArgsManager : : DEBUG_ONLY ) ) {
2018-05-16 15:15:18 -04:00
std : : string name ;
if ( arg . second . m_help_param . empty ( ) ) {
name = arg . first ;
} else {
name = arg . first + arg . second . m_help_param ;
}
usage + = HelpMessageOpt ( name , arg . second . m_help_text ) ;
}
2018-04-28 16:54:58 -04:00
}
}
return usage ;
}
2018-03-30 13:47:36 -07:00
bool HelpRequested ( const ArgsManager & args )
{
2018-09-12 15:01:58 +02:00
return args . IsArgSet ( " -? " ) | | args . IsArgSet ( " -h " ) | | args . IsArgSet ( " -help " ) | | args . IsArgSet ( " -help-debug " ) ;
2018-03-30 13:47:36 -07:00
}
2016-12-24 11:28:44 -05:00
2019-02-06 13:57:52 -05:00
void SetupHelpOptions ( ArgsManager & args )
{
scripted-diff: Use ArgsManager::DEBUG_ONLY flag
-BEGIN VERIFY SCRIPT-
sed -i 's/unsigned int flags, const bool debug_only,/unsigned int flags,/' src/util/system.h src/util/system.cpp
sed -i 's/ArgsManager::NONE, debug_only/flags, false/' src/util/system.cpp
sed -i 's/arg.second.m_debug_only/(arg.second.m_flags \& ArgsManager::DEBUG_ONLY)/' src/util/system.cpp
sed -i 's/ArgsManager::ALLOW_ANY, true, OptionsCategory::/ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::/' $(git grep --files-with-matches 'AddArg(' src)
sed -i 's/ArgsManager::ALLOW_ANY, false, OptionsCategory::/ArgsManager::ALLOW_ANY, OptionsCategory::/' $(git grep --files-with-matches 'AddArg(' src)
-END VERIFY SCRIPT-
2019-07-27 12:06:32 +03:00
args . AddArg ( " -? " , " Print this help message and exit " , ArgsManager : : ALLOW_ANY , OptionsCategory : : OPTIONS ) ;
2019-02-06 13:57:52 -05:00
args . AddHiddenArgs ( { " -h " , " -help " } ) ;
}
2015-02-04 00:11:49 -08:00
static const int screenWidth = 79 ;
static const int optIndent = 2 ;
static const int msgIndent = 7 ;
std : : string HelpMessageGroup ( const std : : string & message ) {
return std : : string ( message ) + std : : string ( " \n \n " ) ;
}
std : : string HelpMessageOpt ( const std : : string & option , const std : : string & message ) {
return std : : string ( optIndent , ' ' ) + std : : string ( option ) +
std : : string ( " \n " ) + std : : string ( msgIndent , ' ' ) +
FormatParagraph ( message , screenWidth - msgIndent , msgIndent ) +
std : : string ( " \n \n " ) ;
}
2022-09-14 11:00:14 +01:00
static std : : string FormatException ( const std : : exception * pex , std : : string_view thread_name )
2011-05-14 17:25:05 +02:00
{
2011-10-07 11:02:21 -04:00
# ifdef WIN32
2012-04-22 16:22:45 +02:00
char pszModule [ MAX_PATH ] = " " ;
2017-08-07 07:36:37 +02:00
GetModuleFileNameA ( nullptr , pszModule , sizeof ( pszModule ) ) ;
2011-05-14 17:25:05 +02:00
# else
const char * pszModule = " bitcoin " ;
# endif
if ( pex )
2012-05-14 19:53:02 +02:00
return strprintf (
2022-09-14 11:00:14 +01:00
" EXCEPTION: %s \n %s \n %s in %s \n " , typeid ( * pex ) . name ( ) , pex - > what ( ) , pszModule , thread_name ) ;
2011-05-14 17:25:05 +02:00
else
2012-05-14 19:53:02 +02:00
return strprintf (
2022-09-14 11:00:14 +01:00
" UNKNOWN EXCEPTION \n %s in %s \n " , pszModule , thread_name ) ;
2011-05-14 17:25:05 +02:00
}
2022-09-14 11:00:14 +01:00
void PrintExceptionContinue ( const std : : exception * pex , std : : string_view thread_name )
2011-05-14 17:25:05 +02:00
{
2022-09-14 11:00:14 +01:00
std : : string message = FormatException ( pex , thread_name ) ;
2014-01-16 16:15:27 +01:00
LogPrintf ( " \n \n ************************ \n %s \n " , message ) ;
2019-10-28 13:30:20 +01:00
tfm : : format ( std : : cerr , " \n \n ************************ \n %s \n " , message ) ;
2011-05-14 17:25:05 +02:00
}
2017-03-01 17:05:50 +01:00
fs : : path GetDefaultDataDir ( )
2011-05-14 17:25:05 +02:00
{
2020-10-16 18:32:43 +03:00
// Windows: C:\Users\Username\AppData\Roaming\Bitcoin
// macOS: ~/Library/Application Support/Bitcoin
// Unix-like: ~/.bitcoin
2011-10-07 11:02:21 -04:00
# ifdef WIN32
2011-05-14 17:25:05 +02:00
// Windows
2012-04-22 16:22:45 +02:00
return GetSpecialFolderPath ( CSIDL_APPDATA ) / " Bitcoin " ;
2011-05-14 17:25:05 +02:00
# else
2012-04-09 23:50:56 +02:00
fs : : path pathRet ;
2011-05-14 17:25:05 +02:00
char * pszHome = getenv ( " HOME " ) ;
2017-08-07 07:36:37 +02:00
if ( pszHome = = nullptr | | strlen ( pszHome ) = = 0 )
2012-04-09 23:50:56 +02:00
pathRet = fs : : path ( " / " ) ;
else
pathRet = fs : : path ( pszHome ) ;
2011-10-07 11:02:21 -04:00
# ifdef MAC_OSX
2020-10-16 18:32:43 +03:00
// macOS
2016-04-09 14:30:07 +01:00
return pathRet / " Library/Application Support/Bitcoin " ;
2011-05-14 17:25:05 +02:00
# else
2020-10-16 18:32:43 +03:00
// Unix-like
2012-04-09 23:50:56 +02:00
return pathRet / " .bitcoin " ;
2011-05-14 17:25:05 +02:00
# endif
# endif
}
2019-07-24 03:21:25 +03:00
bool CheckDataDirOption ( )
{
2022-02-04 18:23:50 +02:00
const fs : : path datadir { gArgs . GetPathArg ( " -datadir " ) } ;
return datadir . empty ( ) | | fs : : is_directory ( fs : : absolute ( datadir ) ) ;
2019-07-24 03:21:25 +03:00
}
2022-03-25 21:36:22 +01:00
fs : : path GetConfigFile ( const fs : : path & configuration_file_path )
2011-05-14 17:25:05 +02:00
{
2022-03-25 21:36:22 +01:00
return AbsPathForConfigVal ( configuration_file_path , /*net_specific=*/ false ) ;
2011-05-14 17:25:05 +02:00
}
2019-02-04 12:53:19 +09:00
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 )
2018-06-16 02:31:26 +00:00
{
std : : string str , prefix ;
std : : string : : size_type pos ;
2018-08-30 13:04:19 +02:00
int linenr = 1 ;
2018-06-16 02:31:26 +00:00
while ( std : : getline ( stream , str ) ) {
2018-10-16 18:19:40 +13:00
bool used_hash = false ;
2018-06-16 02:31:26 +00:00
if ( ( pos = str . find ( ' # ' ) ) ! = std : : string : : npos ) {
str = str . substr ( 0 , pos ) ;
2018-10-16 18:19:40 +13:00
used_hash = true ;
2018-06-16 02:31:26 +00:00
}
const static std : : string pattern = " \t \r \n " ;
str = TrimString ( str , pattern ) ;
if ( ! str . empty ( ) ) {
if ( * str . begin ( ) = = ' [ ' & & * str . rbegin ( ) = = ' ] ' ) {
2018-11-12 11:06:36 +09:00
const std : : string section = str . substr ( 1 , str . size ( ) - 2 ) ;
2019-02-04 12:53:19 +09:00
sections . emplace_back ( SectionInfo { section , filepath , linenr } ) ;
2018-11-12 11:06:36 +09:00
prefix = section + ' . ' ;
2018-08-30 13:04:19 +02:00
} else if ( * str . begin ( ) = = ' - ' ) {
error = strprintf ( " parse error on line %i: %s, options in configuration file must be specified without leading - " , linenr , str ) ;
return false ;
2018-06-16 02:31:26 +00:00
} else if ( ( pos = str . find ( ' = ' ) ) ! = std : : string : : npos ) {
2022-04-04 15:05:47 -04:00
std : : string name = prefix + TrimString ( std : : string_view { str } . substr ( 0 , pos ) , pattern ) ;
std : : string_view value = TrimStringView ( std : : string_view { str } . substr ( pos + 1 ) , pattern ) ;
2019-01-03 23:54:19 +13:00
if ( used_hash & & name . find ( " rpcpassword " ) ! = std : : string : : npos ) {
2018-10-16 18:19:40 +13:00
error = strprintf ( " parse error on line %i, using # in rpcpassword can be ambiguous and should be avoided " , linenr ) ;
return false ;
}
2018-06-16 02:31:26 +00:00
options . emplace_back ( name , value ) ;
2019-02-04 12:53:19 +09:00
if ( ( pos = name . rfind ( ' . ' ) ) ! = std : : string : : npos & & prefix . length ( ) < = pos ) {
sections . emplace_back ( SectionInfo { name . substr ( 0 , pos ) , filepath , linenr } ) ;
2018-11-12 11:06:36 +09:00
}
2018-08-30 13:04:19 +02:00
} else {
error = strprintf ( " parse error on line %i: %s " , linenr , str ) ;
if ( str . size ( ) > = 2 & & str . substr ( 0 , 2 ) = = " no " ) {
error + = strprintf ( " , if you intended to specify a negated option, use %s=1 instead " , str ) ;
}
return false ;
2018-06-16 02:31:26 +00:00
}
}
2018-08-30 13:04:19 +02:00
+ + linenr ;
2018-06-16 02:31:26 +00:00
}
2018-08-30 13:04:19 +02:00
return true ;
2018-06-16 02:31:26 +00:00
}
2022-07-28 12:47:53 +02:00
bool IsConfSupported ( KeyInfo & key , std : : string & error ) {
if ( key . name = = " conf " ) {
error = " conf cannot be set in the configuration file; use includeconf= if you want to include additional config files " ;
return false ;
}
2022-07-29 13:56:43 +02:00
if ( key . name = = " reindex " ) {
// reindex can be set in a config file but it is strongly discouraged as this will cause the node to reindex on
// every restart. Allow the config but throw a warning
LogPrintf ( " Warning: reindex=1 is set in the configuration file, which will significantly slow down startup. Consider removing or commenting out this option for better performance, unless there is currently a condition which makes rebuilding the indexes necessary \n " ) ;
return true ;
}
2022-07-28 12:47:53 +02:00
return true ;
}
2019-02-04 12:53:19 +09:00
bool ArgsManager : : ReadConfigStream ( std : : istream & stream , const std : : string & filepath , std : : string & error , bool ignore_invalid_keys )
2011-05-14 17:25:05 +02:00
{
2018-03-29 15:03:00 +10:00
LOCK ( cs_args ) ;
2018-08-30 13:04:19 +02:00
std : : vector < std : : pair < std : : string , std : : string > > options ;
2019-02-04 12:53:19 +09:00
if ( ! GetConfigOptions ( stream , filepath , error , options , m_config_sections ) ) {
2018-08-30 13:04:19 +02:00
return false ;
}
for ( const std : : pair < std : : string , std : : string > & option : options ) {
2021-08-21 14:06:49 -04:00
KeyInfo key = InterpretKey ( option . first ) ;
std : : optional < unsigned int > flags = GetArgFlags ( ' - ' + key . name ) ;
2022-07-28 12:47:53 +02:00
if ( ! IsConfSupported ( key , error ) ) return false ;
2019-07-27 14:41:27 +03:00
if ( flags ) {
2022-04-12 03:00:28 -04:00
std : : optional < util : : SettingsValue > value = InterpretValue ( key , & option . second , * flags , error ) ;
2021-08-21 14:06:49 -04:00
if ( ! value ) {
2019-07-27 16:19:17 +03:00
return false ;
}
2021-08-21 14:06:49 -04:00
m_settings . ro_config [ key . section ] [ key . name ] . push_back ( * value ) ;
2019-07-27 14:41:27 +03:00
} else {
if ( ignore_invalid_keys ) {
LogPrintf ( " Ignoring unknown configuration value %s \n " , option . first ) ;
} else {
2019-10-28 13:30:20 +01:00
error = strprintf ( " Invalid configuration value %s " , option . first ) ;
2018-07-30 00:14:18 -07:00
return false ;
}
2018-04-28 19:40:51 -04:00
}
2011-05-14 17:25:05 +02:00
}
2018-04-28 19:40:51 -04:00
return true ;
2018-03-29 15:02:00 +10:00
}
2016-11-29 18:52:44 -08:00
2018-04-28 19:40:51 -04:00
bool ArgsManager : : ReadConfigFiles ( std : : string & error , bool ignore_invalid_keys )
2018-03-29 15:02:00 +10:00
{
2018-08-30 10:02:49 +02:00
{
LOCK ( cs_args ) ;
2019-04-22 18:08:51 -04:00
m_settings . ro_config . clear ( ) ;
2019-02-04 12:53:19 +09:00
m_config_sections . clear ( ) ;
2018-08-30 10:02:49 +02:00
}
2018-04-04 18:01:00 +10:00
2022-03-25 21:36:22 +01:00
const fs : : path conf_path = GetPathArg ( " -conf " , BITCOIN_CONF_FILENAME ) ;
std : : ifstream stream { GetConfigFile ( conf_path ) } ;
2018-03-29 15:03:00 +10:00
2021-08-04 12:07:31 +02:00
// not ok to have a config file specified that cannot be opened
if ( IsArgSet ( " -conf " ) & & ! stream . good ( ) ) {
2022-03-25 21:36:22 +01:00
error = strprintf ( " specified config file \" %s \" could not be opened. " , fs : : PathToString ( conf_path ) ) ;
2021-08-04 12:07:31 +02:00
return false ;
}
2018-03-29 15:03:00 +10:00
// ok to not have a config file
if ( stream . good ( ) ) {
2022-03-25 21:36:22 +01:00
if ( ! ReadConfigStream ( stream , fs : : PathToString ( conf_path ) , error , ignore_invalid_keys ) ) {
2018-04-28 19:40:51 -04:00
return false ;
}
2019-11-07 22:01:22 -05:00
// `-includeconf` cannot be included in the command line arguments except
2019-11-11 18:40:52 -05:00
// as `-noincludeconf` (which indicates that no included conf file should be used).
2019-11-07 22:01:22 -05:00
bool use_conf_file { true } ;
2018-08-30 10:02:49 +02:00
{
LOCK ( cs_args ) ;
2019-04-22 18:08:51 -04:00
if ( auto * includes = util : : FindKey ( m_settings . command_line_options , " includeconf " ) ) {
2019-11-07 22:01:22 -05:00
// ParseParameters() fails if a non-negated -includeconf is passed on the command-line
2019-04-22 18:08:51 -04:00
assert ( util : : SettingsSpan ( * includes ) . last_negated ( ) ) ;
2019-11-07 22:01:22 -05:00
use_conf_file = false ;
}
2018-08-30 10:02:49 +02:00
}
2019-11-07 22:01:22 -05:00
if ( use_conf_file ) {
2018-06-01 13:28:14 -04:00
std : : string chain_id = GetChainName ( ) ;
2019-04-22 18:08:51 -04:00
std : : vector < std : : string > conf_file_names ;
2018-04-18 14:35:44 +09:00
2019-04-22 18:08:51 -04:00
auto add_includes = [ & ] ( const std : : string & network , size_t skip = 0 ) {
size_t num_values = 0 ;
2018-08-30 10:02:49 +02:00
LOCK ( cs_args ) ;
2019-04-22 18:08:51 -04:00
if ( auto * section = util : : FindKey ( m_settings . ro_config , network ) ) {
if ( auto * values = util : : FindKey ( * section , " includeconf " ) ) {
for ( size_t i = std : : max ( skip , util : : SettingsSpan ( * values ) . negated ( ) ) ; i < values - > size ( ) ; + + i ) {
conf_file_names . push_back ( ( * values ) [ i ] . get_str ( ) ) ;
}
num_values = values - > size ( ) ;
}
}
return num_values ;
} ;
// We haven't set m_network yet (that happens in SelectParams()), so manually check
// for network.includeconf args.
const size_t chain_includes = add_includes ( chain_id ) ;
const size_t default_includes = add_includes ( { } ) ;
2018-05-09 16:49:59 +09:00
2019-11-07 22:04:42 -05:00
for ( const std : : string & conf_file_name : conf_file_names ) {
2022-03-25 21:36:22 +01:00
std : : ifstream conf_file_stream { GetConfigFile ( fs : : PathFromString ( conf_file_name ) ) } ;
2019-11-07 22:04:42 -05:00
if ( conf_file_stream . good ( ) ) {
if ( ! ReadConfigStream ( conf_file_stream , conf_file_name , error , ignore_invalid_keys ) ) {
2018-04-28 19:40:51 -04:00
return false ;
}
2019-11-07 22:04:42 -05:00
LogPrintf ( " Included configuration file %s \n " , conf_file_name ) ;
2018-04-18 14:35:44 +09:00
} else {
2019-11-07 22:04:42 -05:00
error = " Failed to include configuration file " + conf_file_name ;
2018-06-01 13:28:14 -04:00
return false ;
2018-04-18 14:35:44 +09:00
}
}
2018-05-09 16:49:59 +09:00
// Warn about recursive -includeconf
2019-04-22 18:08:51 -04:00
conf_file_names . clear ( ) ;
add_includes ( chain_id , /* skip= */ chain_includes ) ;
add_includes ( { } , /* skip= */ default_includes ) ;
2019-11-07 22:08:22 -05:00
std : : string chain_id_final = GetChainName ( ) ;
if ( chain_id_final ! = chain_id ) {
// Also warn about recursive includeconf for the chain that was specified in one of the includeconfs
2019-04-22 18:08:51 -04:00
add_includes ( chain_id_final ) ;
2018-05-09 16:49:59 +09:00
}
2019-11-07 22:04:42 -05:00
for ( const std : : string & conf_file_name : conf_file_names ) {
tfm : : format ( std : : cerr , " warning: -includeconf cannot be used from included files; ignoring -includeconf=%s \n " , conf_file_name ) ;
2018-05-09 16:49:59 +09:00
}
2018-04-18 14:35:44 +09:00
}
2011-05-14 17:25:05 +02:00
}
2018-03-29 15:02:00 +10:00
2013-07-17 12:20:09 +10:00
// If datadir is changed in .conf file:
2021-02-26 09:52:50 +01:00
gArgs . ClearPathCache ( ) ;
2019-07-24 03:29:40 +03:00
if ( ! CheckDataDirOption ( ) ) {
2020-10-06 14:36:50 +03:00
error = strprintf ( " specified data directory \" %s \" does not exist. " , GetArg ( " -datadir " , " " ) ) ;
2018-04-28 19:40:51 -04:00
return false ;
2017-12-06 00:24:06 +13:00
}
2018-04-28 19:40:51 -04:00
return true ;
2011-05-14 17:25:05 +02:00
}
2018-03-29 15:00:00 +10:00
std : : string ArgsManager : : GetChainName ( ) const
{
2019-04-22 18:08:51 -04:00
auto get_net = [ & ] ( const std : : string & arg ) {
LOCK ( cs_args ) ;
2019-11-12 13:47:19 -05:00
util : : SettingsValue value = util : : GetSetting ( m_settings , /* section= */ " " , SettingName ( arg ) ,
/* ignore_default_section_config= */ false ,
2019-04-29 15:29:00 -04:00
/*ignore_nonpersistent=*/ false ,
2019-11-12 13:47:19 -05:00
/* get_chain_name= */ true ) ;
2019-04-22 18:08:51 -04:00
return value . isNull ( ) ? false : value . isBool ( ) ? value . get_bool ( ) : InterpretBool ( value . get_str ( ) ) ;
} ;
const bool fRegTest = get_net ( " -regtest " ) ;
2020-03-05 15:58:30 +09:00
const bool fSigNet = get_net ( " -signet " ) ;
2019-04-22 18:08:51 -04:00
const bool fTestNet = get_net ( " -testnet " ) ;
2016-10-13 22:38:10 +02:00
const bool is_chain_arg_set = IsArgSet ( " -chain " ) ;
2018-03-29 15:00:00 +10:00
2020-03-05 15:58:30 +09:00
if ( ( int ) is_chain_arg_set + ( int ) fRegTest + ( int ) fSigNet + ( int ) fTestNet > 1 ) {
throw std : : runtime_error ( " Invalid combination of -regtest, -signet, -testnet and -chain. Can use at most one. " ) ;
2016-10-13 22:38:10 +02:00
}
2018-03-29 15:00:00 +10:00
if ( fRegTest )
return CBaseChainParams : : REGTEST ;
2020-03-05 15:58:30 +09:00
if ( fSigNet ) {
return CBaseChainParams : : SIGNET ;
}
2018-03-29 15:00:00 +10:00
if ( fTestNet )
return CBaseChainParams : : TESTNET ;
2020-03-05 15:58:30 +09:00
2016-10-13 22:38:10 +02:00
return GetArg ( " -chain " , CBaseChainParams : : MAIN ) ;
2018-03-29 15:00:00 +10:00
}
2019-11-12 13:47:19 -05:00
bool ArgsManager : : UseDefaultSection ( const std : : string & arg ) const
{
return m_network = = CBaseChainParams : : MAIN | | m_network_only_args . count ( arg ) = = 0 ;
}
util : : SettingsValue ArgsManager : : GetSetting ( const std : : string & arg ) const
{
LOCK ( cs_args ) ;
return util : : GetSetting (
2019-04-29 15:29:00 -04:00
m_settings , m_network , SettingName ( arg ) , ! UseDefaultSection ( arg ) ,
/*ignore_nonpersistent=*/ false , /*get_chain_name=*/ false ) ;
2019-11-12 13:47:19 -05:00
}
2019-11-12 13:56:18 -05:00
std : : vector < util : : SettingsValue > ArgsManager : : GetSettingsList ( const std : : string & arg ) const
{
LOCK ( cs_args ) ;
return util : : GetSettingsList ( m_settings , m_network , SettingName ( arg ) , ! UseDefaultSection ( arg ) ) ;
}
2020-01-20 08:32:42 -07:00
void ArgsManager : : logArgsPrefix (
const std : : string & prefix ,
const std : : string & section ,
const std : : map < std : : string , std : : vector < util : : SettingsValue > > & args ) const
{
std : : string section_str = section . empty ( ) ? " " : " [ " + section + " ] " ;
for ( const auto & arg : args ) {
for ( const auto & value : arg . second ) {
2021-03-15 10:41:30 +08:00
std : : optional < unsigned int > flags = GetArgFlags ( ' - ' + arg . first ) ;
2020-01-20 08:32:42 -07:00
if ( flags ) {
std : : string value_str = ( * flags & SENSITIVE ) ? " **** " : value . write ( ) ;
LogPrintf ( " %s %s%s=%s \n " , prefix , section_str , arg . first , value_str ) ;
}
}
}
}
void ArgsManager : : LogArgs ( ) const
{
LOCK ( cs_args ) ;
for ( const auto & section : m_settings . ro_config ) {
logArgsPrefix ( " Config file arg: " , section . first , section . second ) ;
}
2019-04-28 19:08:26 -04:00
for ( const auto & setting : m_settings . rw_settings ) {
LogPrintf ( " Setting file arg: %s = %s \n " , setting . first , setting . second . write ( ) ) ;
}
2020-01-20 08:32:42 -07:00
logArgsPrefix ( " Command-line arg: " , " " , m_settings . command_line_options ) ;
}
2017-03-01 17:05:50 +01:00
bool RenameOver ( fs : : path src , fs : : path dest )
2012-05-12 01:24:27 -04:00
{
2022-02-13 17:30:21 +02:00
# ifdef __MINGW64__
// This is a workaround for a bug in libstdc++ which
// implements std::filesystem::rename with _wrename function.
// This bug has been fixed in upstream:
// - GCC 10.3: 8dd1c1085587c9f8a21bb5e588dfe1e8cdbba79e
// - GCC 11.1: 1dfd95f0a0ca1d9e6cbc00e6cbfd1fa20a98f312
// For more details see the commits mentioned above.
return MoveFileExW ( src . wstring ( ) . c_str ( ) , dest . wstring ( ) . c_str ( ) ,
MOVEFILE_REPLACE_EXISTING ) ! = 0 ;
# else
2020-03-14 20:58:55 +01:00
std : : error_code error ;
fs : : rename ( src , dest , error ) ;
return ! error ;
2022-02-13 17:30:21 +02:00
# endif
2012-05-12 01:24:27 -04:00
}
2014-11-17 11:04:01 +08:00
/**
2020-06-11 08:58:46 +02:00
* Ignores exceptions thrown by create_directories if the requested directory exists .
2014-11-17 11:04:01 +08:00
* Specifically handles case where path p exists , but it wasn ' t possible for the user to
* write to the parent directory .
*/
2017-02-22 18:10:00 +09:00
bool TryCreateDirectories ( const fs : : path & p )
2014-03-23 20:14:43 -05:00
{
try
{
2017-02-22 18:10:00 +09:00
return fs : : create_directories ( p ) ;
2017-03-01 17:05:50 +01:00
} catch ( const fs : : filesystem_error & ) {
if ( ! fs : : exists ( p ) | | ! fs : : is_directory ( p ) )
2014-03-23 20:14:43 -05:00
throw ;
}
2017-02-22 18:10:00 +09:00
// create_directories didn't create the directory, it had to have existed already
2014-03-23 20:14:43 -05:00
return false ;
}
2018-04-20 11:21:08 +02:00
bool FileCommit ( FILE * file )
2012-05-12 01:24:27 -04:00
{
2018-04-20 11:21:08 +02:00
if ( fflush ( file ) ! = 0 ) { // harmless if redundantly called
LogPrintf ( " %s: fflush failed: %d \n " , __func__ , errno ) ;
return false ;
}
2012-05-12 01:24:27 -04:00
# ifdef WIN32
2016-09-02 20:50:59 +02:00
HANDLE hFile = ( HANDLE ) _get_osfhandle ( _fileno ( file ) ) ;
2018-04-20 11:21:08 +02:00
if ( FlushFileBuffers ( hFile ) = = 0 ) {
LogPrintf ( " %s: FlushFileBuffers failed: %d \n " , __func__ , GetLastError ( ) ) ;
return false ;
}
2018-03-15 06:54:11 -07:00
# elif defined(MAC_OSX) && defined(F_FULLFSYNC)
2018-04-20 11:21:08 +02:00
if ( fcntl ( fileno ( file ) , F_FULLFSYNC , 0 ) = = - 1 ) { // Manpage says "value other than -1" is returned on success
LogPrintf ( " %s: fcntl F_FULLFSYNC failed: %d \n " , __func__ , errno ) ;
return false ;
}
2018-03-15 06:54:11 -07:00
# elif HAVE_FDATASYNC
if ( fdatasync ( fileno ( file ) ) ! = 0 & & errno ! = EINVAL ) { // Ignore EINVAL for filesystems that don't support sync
LogPrintf ( " %s: fdatasync failed: %d \n " , __func__ , errno ) ;
return false ;
}
2018-03-15 06:54:11 -07:00
# else
2018-04-20 11:21:08 +02:00
if ( fsync ( fileno ( file ) ) ! = 0 & & errno ! = EINVAL ) {
LogPrintf ( " %s: fsync failed: %d \n " , __func__ , errno ) ;
return false ;
}
2012-05-12 01:24:27 -04:00
# endif
2018-04-20 11:21:08 +02:00
return true ;
2012-05-12 01:24:27 -04:00
}
2018-03-15 06:54:11 -07:00
void DirectoryCommit ( const fs : : path & dirname )
{
# ifndef WIN32
FILE * file = fsbridge : : fopen ( dirname , " r " ) ;
2018-10-17 08:34:53 +00:00
if ( file ) {
fsync ( fileno ( file ) ) ;
fclose ( file ) ;
}
2018-03-15 06:54:11 -07:00
# endif
}
2013-01-30 04:17:33 +01:00
bool TruncateFile ( FILE * file , unsigned int length ) {
# if defined(WIN32)
return _chsize ( _fileno ( file ) , length ) = = 0 ;
# else
return ftruncate ( fileno ( file ) , length ) = = 0 ;
# endif
}
2014-11-17 11:04:01 +08:00
/**
* this function tries to raise the file descriptor limit to the requested number .
* It returns the actual file descriptor limit ( which may be more or less than nMinFD )
*/
2013-04-26 00:46:47 +02:00
int RaiseFileDescriptorLimit ( int nMinFD ) {
# if defined(WIN32)
return 2048 ;
# else
struct rlimit limitFD ;
if ( getrlimit ( RLIMIT_NOFILE , & limitFD ) ! = - 1 ) {
if ( limitFD . rlim_cur < ( rlim_t ) nMinFD ) {
limitFD . rlim_cur = nMinFD ;
if ( limitFD . rlim_cur > limitFD . rlim_max )
limitFD . rlim_cur = limitFD . rlim_max ;
setrlimit ( RLIMIT_NOFILE , & limitFD ) ;
getrlimit ( RLIMIT_NOFILE , & limitFD ) ;
}
return limitFD . rlim_cur ;
}
return nMinFD ; // getrlimit failed, assume it's fine
# endif
}
2014-11-17 11:04:01 +08:00
/**
* this function tries to make a particular range of a file allocated ( corresponding to disk space )
* it is advisory , and the range specified in the arguments will never contain live data
*/
2012-08-16 02:21:28 +02:00
void AllocateFileRange ( FILE * file , unsigned int offset , unsigned int length ) {
2013-01-28 00:07:51 +01:00
# if defined(WIN32)
// Windows-specific version
HANDLE hFile = ( HANDLE ) _get_osfhandle ( _fileno ( file ) ) ;
LARGE_INTEGER nFileSize ;
2013-04-13 00:13:08 -05:00
int64_t nEndPos = ( int64_t ) offset + length ;
2013-01-28 00:07:51 +01:00
nFileSize . u . LowPart = nEndPos & 0xFFFFFFFF ;
nFileSize . u . HighPart = nEndPos > > 32 ;
SetFilePointerEx ( hFile , nFileSize , 0 , FILE_BEGIN ) ;
SetEndOfFile ( hFile ) ;
# elif defined(MAC_OSX)
// OSX specific version
2020-01-07 22:24:16 +09:00
// NOTE: Contrary to other OS versions, the OSX version assumes that
// NOTE: offset is the size of the file.
2013-01-28 00:07:51 +01:00
fstore_t fst ;
fst . fst_flags = F_ALLOCATECONTIG ;
fst . fst_posmode = F_PEOFPOSMODE ;
fst . fst_offset = 0 ;
2020-01-07 22:24:16 +09:00
fst . fst_length = length ; // mac os fst_length takes the # of free bytes to allocate, not desired file size
2013-01-28 00:07:51 +01:00
fst . fst_bytesalloc = 0 ;
if ( fcntl ( fileno ( file ) , F_PREALLOCATE , & fst ) = = - 1 ) {
fst . fst_flags = F_ALLOCATEALL ;
fcntl ( fileno ( file ) , F_PREALLOCATE , & fst ) ;
}
2020-01-07 22:24:16 +09:00
ftruncate ( fileno ( file ) , static_cast < off_t > ( offset ) + length ) ;
2019-03-23 04:20:40 +00:00
# else
2020-03-26 10:59:46 +01:00
# if defined(HAVE_POSIX_FALLOCATE)
2013-01-28 00:07:51 +01:00
// Version using posix_fallocate
off_t nEndPos = ( off_t ) offset + length ;
2019-03-23 04:20:40 +00:00
if ( 0 = = posix_fallocate ( fileno ( file ) , 0 , nEndPos ) ) return ;
# endif
2013-01-28 00:07:51 +01:00
// Fallback version
// TODO: just write one byte per block
2012-08-16 02:21:28 +02:00
static const char buf [ 65536 ] = { } ;
2018-05-02 12:12:55 +02:00
if ( fseek ( file , offset , SEEK_SET ) ) {
return ;
}
2012-08-16 02:21:28 +02:00
while ( length > 0 ) {
unsigned int now = 65536 ;
if ( length < now )
now = length ;
fwrite ( buf , 1 , now , file ) ; // allowed to fail; this function is advisory anyway
length - = now ;
}
2013-01-28 00:07:51 +01:00
# endif
2012-08-16 02:21:28 +02:00
}
2012-04-15 22:10:54 +02:00
# ifdef WIN32
2017-03-01 17:05:50 +01:00
fs : : path GetSpecialFolderPath ( int nFolder , bool fCreate )
2012-04-22 16:22:45 +02:00
{
2018-07-31 23:03:52 +08:00
WCHAR pszPath [ MAX_PATH ] = L " " ;
2012-04-22 16:22:45 +02:00
2018-07-31 23:03:52 +08:00
if ( SHGetSpecialFolderPathW ( nullptr , pszPath , nFolder , fCreate ) )
2012-04-22 16:22:45 +02:00
{
return fs : : path ( pszPath ) ;
}
2018-07-31 23:03:52 +08:00
LogPrintf ( " SHGetSpecialFolderPathW() failed, could not obtain requested path. \n " ) ;
2012-04-22 16:22:45 +02:00
return fs : : path ( " " ) ;
}
2012-04-15 22:10:54 +02:00
# endif
2012-05-23 23:10:59 -04:00
2018-05-29 14:37:53 +01:00
# ifndef WIN32
std : : string ShellEscape ( const std : : string & arg )
{
std : : string escaped = arg ;
2022-05-05 08:28:29 +02:00
ReplaceAll ( escaped , " ' " , " ' \" ' \" ' " ) ;
2018-05-29 14:37:53 +01:00
return " ' " + escaped + " ' " ;
}
# endif
2019-07-05 18:30:15 +02:00
# if HAVE_SYSTEM
2015-05-31 15:36:44 +02:00
void runCommand ( const std : : string & strCommand )
2012-05-23 23:10:59 -04:00
{
2017-07-27 11:35:01 +02:00
if ( strCommand . empty ( ) ) return ;
2018-08-06 01:03:33 +00:00
# ifndef WIN32
2012-05-23 23:10:59 -04:00
int nErr = : : system ( strCommand . c_str ( ) ) ;
2018-08-06 01:03:33 +00:00
# else
int nErr = : : _wsystem ( std : : wstring_convert < std : : codecvt_utf8_utf16 < wchar_t > , wchar_t > ( ) . from_bytes ( strCommand ) . c_str ( ) ) ;
# endif
2012-05-23 23:10:59 -04:00
if ( nErr )
2014-01-16 16:15:27 +01:00
LogPrintf ( " runCommand error: system(%s) returned %d \n " , strCommand , nErr ) ;
2012-05-23 23:10:59 -04:00
}
2019-03-14 11:30:37 +01:00
# endif
2012-05-23 23:10:59 -04:00
2020-01-30 12:07:57 +01:00
2014-05-13 10:15:00 +00:00
void SetupEnvironment ( )
{
2017-03-30 09:37:47 +02:00
# ifdef HAVE_MALLOPT_ARENA_MAX
// glibc-specific: On 32-bit systems set the number of arenas to 1.
// By default, since glibc 2.10, the C library will create up to two heap
// arenas per core. This is known to cause excessive virtual address space
// usage in our usage. Work around it by setting the maximum number of
// arenas to 1.
if ( sizeof ( void * ) = = 4 ) {
mallopt ( M_ARENA_MAX , 1 ) ;
}
# endif
2015-03-11 13:34:20 +01:00
// On most POSIX systems (e.g. Linux, but not BSD) the environment's locale
2019-10-09 14:53:18 +02:00
// may be invalid, in which case the "C.UTF-8" locale is used as fallback.
2021-07-02 11:10:25 +08:00
# if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
2015-03-11 13:34:20 +01:00
try {
2015-05-02 10:10:31 +02:00
std : : locale ( " " ) ; // Raises a runtime error if current locale is invalid
2014-12-07 13:29:06 +01:00
} catch ( const std : : runtime_error & ) {
2019-10-09 14:53:18 +02:00
setenv ( " LC_ALL " , " C.UTF-8 " , 1 ) ;
2014-05-13 10:15:00 +00:00
}
2018-08-05 16:38:25 +00:00
# elif defined(WIN32)
// Set the default input/output charset is utf-8
SetConsoleCP ( CP_UTF8 ) ;
SetConsoleOutputCP ( CP_UTF8 ) ;
2018-08-04 16:39:14 +00:00
# endif
2014-05-13 10:15:00 +00:00
}
2014-05-08 18:01:10 +02:00
2015-09-02 16:18:16 +02:00
bool SetupNetworking ( )
{
# ifdef WIN32
// Initialize Windows Sockets
WSADATA wsadata ;
int ret = WSAStartup ( MAKEWORD ( 2 , 2 ) , & wsadata ) ;
if ( ret ! = NO_ERROR | | LOBYTE ( wsadata . wVersion ) ! = 2 | | HIBYTE ( wsadata . wVersion ) ! = 2 )
return false ;
# endif
return true ;
}
2015-07-01 17:38:15 +02:00
int GetNumCores ( )
{
2017-04-25 09:34:23 +08:00
return std : : thread : : hardware_concurrency ( ) ;
2015-07-01 17:38:15 +02:00
}
2017-05-14 19:18:26 +01:00
// Obtain the application startup time (used for uptime calculation)
int64_t GetStartupTime ( )
{
return nStartupTime ;
}
2018-01-30 22:33:49 -05:00
fs : : path AbsPathForConfigVal ( const fs : : path & path , bool net_specific )
{
2019-04-29 22:46:34 +03:00
if ( path . is_absolute ( ) ) {
return path ;
}
2021-05-22 14:55:38 +02:00
return fsbridge : : AbsPathJoin ( net_specific ? gArgs . GetDataDirNet ( ) : gArgs . GetDataDirBase ( ) , path ) ;
2018-01-30 22:33:49 -05:00
}
2018-03-06 12:50:20 -05:00
2019-11-25 17:31:31 -05:00
void ScheduleBatchPriority ( )
2018-03-06 12:50:20 -05:00
{
# ifdef SCHED_BATCH
2018-09-27 18:08:39 -04:00
const static sched_param param { } ;
2020-06-08 16:37:59 +09:00
const int rc = pthread_setschedparam ( pthread_self ( ) , SCHED_BATCH , & param ) ;
if ( rc ! = 0 ) {
2022-04-20 16:17:19 +02:00
LogPrintf ( " Failed to pthread_setschedparam: %s \n " , SysErrorString ( rc ) ) ;
2018-03-06 12:50:20 -05:00
}
# endif
}
2018-08-05 16:38:25 +00:00
namespace util {
# ifdef WIN32
WinCmdLineArgs : : WinCmdLineArgs ( )
{
wchar_t * * wargv = CommandLineToArgvW ( GetCommandLineW ( ) , & argc ) ;
std : : wstring_convert < std : : codecvt_utf8_utf16 < wchar_t > , wchar_t > utf8_cvt ;
argv = new char * [ argc ] ;
args . resize ( argc ) ;
for ( int i = 0 ; i < argc ; i + + ) {
args [ i ] = utf8_cvt . to_bytes ( wargv [ i ] ) ;
argv [ i ] = & * args [ i ] . begin ( ) ;
}
LocalFree ( wargv ) ;
}
WinCmdLineArgs : : ~ WinCmdLineArgs ( )
{
delete [ ] argv ;
}
std : : pair < int , char * * > WinCmdLineArgs : : get ( )
{
return std : : make_pair ( argc , argv ) ;
}
# endif
} // namespace util