diff --git a/doc/release-notes.md b/doc/release-notes.md --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -21,3 +21,9 @@ - The `getmininginfo` RPC now omits `currentblocksize` and `currentblocktx` when a block was never assembled via RPC on this node. +Seeder +------ + - Added the `seederdir` option to the seeder allowing a directory to be + specified for the files the seeder writes to and reads from (default is the + `~/.bitcoin` directory). See help for more details. + diff --git a/src/seeder/main.cpp b/src/seeder/main.cpp --- a/src/seeder/main.cpp +++ b/src/seeder/main.cpp @@ -94,6 +94,26 @@ ipv6_proxy = gArgs.GetArg("-proxyipv6", DEFAULT_IPV6_PROXY); SelectParams(gArgs.GetChainName()); + if (!CheckSeederDirOption()) { + tfm::format(std::cout, + "Specified data directory \"%s\" does not exist.\n", + gArgs.GetArg("-seederdir", "")); + return EXIT_FAILURE; + } + + // Warn about relative -seederdir path + if (gArgs.IsArgSet("-seederdir") && + !fs::path(gArgs.GetArg("-seederdir", "")).is_absolute()) { + tfm::format( + std::cout, + "Warning: relative seederdir option '%s' specified, which will " + "be interpreted relative to the current working directory " + "'%s'. This is fragile, because if the seeder is started in " + "the future from a different location, it will be unable to " + "locate the current data files.\n", + gArgs.GetArg("-seederdir", ""), fs::current_path().string()); + } + if (gArgs.IsArgSet("-filter")) { // Parse whitelist additions std::string flagString = gArgs.GetArg("-filter", ""); @@ -154,6 +174,10 @@ gArgs.AddArg("-help-debug", "Show all debugging options (usage: --help -help-debug)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-seederdir=", + "Specify directory to hold the subdirectory for files the " + "seeder writes to and reads from (default: ~/.bitcoin/", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); SetupChainParamsBaseOptions(); gArgs.AddArg("-help", "", ArgsManager::ALLOW_ANY, @@ -382,15 +406,16 @@ { std::vector v = db.GetAll(); sort(v.begin(), v.end(), StatCompare); - FILE *f = fsbridge::fopen("dnsseed.dat.new", "w+"); + FILE *f = fsbridge::fopen(GetSeederDir() / "dnsseed.dat.new", "w+"); if (f) { { CAutoFile cf(f, SER_DISK, CLIENT_VERSION); cf << db; } - rename("dnsseed.dat.new", "dnsseed.dat"); + rename(GetSeederDir() / "dnsseed.dat.new", + GetSeederDir() / "dnsseed.dat"); } - fsbridge::ofstream d{"dnsseed.dump"}; + fsbridge::ofstream d{GetSeederDir() / "dnsseed.dump"}; tfm::format( d, "# address good " "lastSuccess %%(2h) %%(8h) %%(1d) %%(7d) " @@ -413,7 +438,8 @@ stat[3] += rep.uptime[3]; stat[4] += rep.uptime[4]; } - fsbridge::ofstream ff{"dnsstats.log", std::ios_base::app}; + fsbridge::ofstream ff{GetSeederDir() / "dnsstats.log", + std::ios_base::app}; tfm::format(ff, "%llu %g %g %g %g %g\n", (unsigned long long)(time(nullptr)), stat[0], stat[1], stat[2], stat[3], stat[4]); @@ -534,7 +560,8 @@ tfm::format(std::cerr, "No e-mail address set. Please use -m.\n"); return EXIT_FAILURE; } - FILE *f = fsbridge::fopen("dnsseed.dat", "r"); + + FILE *f = fsbridge::fopen(GetSeederDir() / "dnsseed.dat", "r"); if (f) { tfm::format(std::cout, "Loading dnsseed.dat..."); CAutoFile cf(f, SER_DISK, CLIENT_VERSION); diff --git a/src/util/system.h b/src/util/system.h --- a/src/util/system.h +++ b/src/util/system.h @@ -83,10 +83,13 @@ fs::path GetDefaultDataDir(); // The blocks directory is always net specific. const fs::path &GetBlocksDir(); +// The seeder directory is always net specific. +const fs::path &GetSeederDir(); const fs::path &GetDataDir(bool fNetSpecific = true); // Return true if -datadir option points to a valid directory or is not // specified. bool CheckDataDirOption(); +bool CheckSeederDirOption(); /** Tests only */ void ClearDatadirCache(); fs::path GetConfigFile(const std::string &confPath); diff --git a/src/util/system.cpp b/src/util/system.cpp --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -645,6 +645,7 @@ } static fs::path g_blocks_path_cache_net_specific; +static fs::path seederPathCachedNetSpecific; static fs::path pathCached; static fs::path pathCachedNetSpecific; static RecursiveMutex csPathCached; @@ -675,6 +676,31 @@ return path; } +const fs::path &GetSeederDir() { + LOCK(csPathCached); + fs::path &path = seederPathCachedNetSpecific; + + // Cache the path to avoid calling fs::create_directories on every call of + // this function + if (!path.empty()) { + return path; + } + if (gArgs.IsArgSet("-seederdir")) { + path = fs::system_complete(gArgs.GetArg("-seederdir", "")); + if (!fs::is_directory(path)) { + path = ""; + return path; + } + } else { + path = GetDataDir(false); + } + + path /= BaseParams().DataDir(); + path /= "seeder"; + fs::create_directories(path); + return path; +} + const fs::path &GetDataDir(bool fNetSpecific) { LOCK(csPathCached); fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached; @@ -719,12 +745,19 @@ return datadir.empty() || fs::is_directory(fs::system_complete(datadir)); } +bool CheckSeederDirOption() { + std::string seederdir = gArgs.GetArg("-seederdir", ""); + return seederdir.empty() || + fs::is_directory(fs::system_complete(seederdir)); +} + void ClearDatadirCache() { LOCK(csPathCached); pathCached = fs::path(); pathCachedNetSpecific = fs::path(); g_blocks_path_cache_net_specific = fs::path(); + seederPathCachedNetSpecific = fs::path(); } fs::path GetConfigFile(const std::string &confPath) {